从头开始在Python中进行随机梯度下降实现实施正确吗?

如何解决从头开始在Python中进行随机梯度下降实现实施正确吗?

我知道这似乎与之前在同一主题上提出的许多问题相似。我已经调查了其中大多数,但他们并未完全回答我的问题。我的问题是我的梯度没有收敛到最优值,而是在非常低的alpha值下发散和振荡。

我的数据生成功能在下面

X = [[float(np.random.randn(1)) for i in range(0,100)] for j in range(0,5)]
X = np.array(X).transpose()
Y = [float(0) for i in range(0,100)]
Y = 2*X[:,0] + 3*X[:,1] + 1*X[:,2] + 4*X[:,3] + 1*X[:,4] + 5
fig,ax = plt.subplots(1,5)
fig.set_size_inches(20,5)
k = 0
for j in range(0,5):
    sns.scatterplot(X[:,k],Y,ax=ax[j])
    k += 1

我的SGD实施如下

def multilinreg(X,epsilon = 0.000001,alpha = 0.01,K = 20):
    Xnot = [[1] for i in range(0,len(X))]
    Xnot = np.array(Xnot)
    X = np.append(Xnot,X,axis = 1)
    vars = X.shape[1]
    W = []
    W = [np.random.normal(1) for i in range(vars)]
    W = np.array(W)
    J = 0
    for i in range(len(X)):
      Yunit = 0
      for j in range(vars):
        Yunit = Yunit + X[i,j] * W[j]
        J = J + (0.5/(len(X)))*((Y[i]-Yunit)**2)
    err = 1
    iter = 0
    Weights = []
    Weights.append(W)
    Costs = []
    while err > epsilon:
      index = [np.random.randint(len(Y)) for i in range(K)]
      Xsample,Ysample = X[index,:],Y[index]
      m =len(Xsample)
      Ypredsample = []
      for i in range(len(Xsample)):
        Yunit = 0
        for j in range(vars):
          Yunit = Yunit + X[i,j] * W[j]
        Ypredsample.append(Yunit)
      Ypredsample = np.array(Ypredsample)
      for i in range(len(Xsample)):
        for j in range(vars):
          gradJunit = (-1)*(Xsample[i,j]*(Ysample[i] - Ypredsample[i]))
          W[j] = W[j] - alpha*gradJunit
      Jnew = 0
      for i in range(len(Xsample)):
        Yunit = 0
        for j in range(vars):
          Yunit = Yunit + Xsample[i,j]*W[j]
          Jnew = Jnew + (0.5/(len(Xsample)))*((Ysample[i]-Yunit)**2)
      Weights.append(W)
      err = abs(float(Jnew - J))
      J = Jnew 
      Costs.append(J)
      iter += 1
      if iter % 1000 == 0:
        print(iter)
        print(J)
    Costs = np.array(Costs)
    Ypred = []
    for i in range(len(X)):
      Yunit = 0
      for j in range(vars):
        Yunit = Yunit + X[i,j] * W[j]
      Ypred.append(Yunit)
    Ypred = np.array(Ypred)
    return Ypred,iter,Costs,W

超参数如下

epsilon = 1*(10)**(-20)
alpha = 0.0000001
K = 50

我不认为这是数据问题。我使用的是相当简单的线性函数。

我认为这是方程式,但我也对其进行了仔细检查,它们对我来说似乎很好。

解决方法

在您的实现中有几件事需要更正(大多数出于效率考虑)。当然,您只需定义w = np.array([5,2,3,1,4,1])就可以节省时间,但这并不能回答SGD实施为何无效的问题。

首先,您通过执行以下操作来定义X

X = [[float(np.random.randn(1)) for i in range(0,100)] for j in range(0,5)]
X = np.array(X).transpose()

执行此操作的更快方法是:

X = np.random.randn(100,5)

然后,您定义Y

Y = [float(0) for i in range(0,100)]
Y = 2*X[:,0] + 3*X[:,1] + 1*X[:,2] + 4*X[:,3] + 1*X[:,4] + 5

第一次初始化Y = [float(0) for i in range(0,100)]是没有用的,因为您立即用第二行覆盖Y。编写此行的一种更简洁的方法可能是:

Y = X @ np.array([2,1]) + 5

现在,关于您的SGD实施。行:

    Xnot = [[1] for i in range(0,len(X))]
    Xnot = np.array(Xnot)
    X = np.append(Xnot,X,axis = 1)

可以更有效地重写为:

    X = np.hstack((np.ones(len(X)).reshape(-1,1),X))

类似地,线条

    W = []
    W = [np.random.normal(1) for i in range(vars)]
    W = np.array(W)
可以使用numpy函数重写

。请注意,第一行W = []是无用的,因为您在不使用W后立即覆盖了它。 np.random.normal可以使用size关键字参数直接生成多个样本。另外,请注意,在使用np.random.normal(1)时,您是从均值1和std 1的正态分布中采样的,而您可能想从均值0和std 1的正态分布中采样。因此,您应该定义:

    W = np.random.normal(size=vars)

Yunit是您使用W做出的预测。根据定义,您可以通过执行以下操作来计算它:

    Yunit = X @ W

避免嵌套的for循环。尽管您计算J的方式很奇怪。如果我没记错的话,J对应于您的损失函数。但是,假设MSE损失为J,则J = 0.5 * sum from k=1 to len(X) of (y_k - w*x_k) ** 2的公式。因此,这两个嵌套的for循环可以重写为:

    Yunit = X @ W
    J = 0.5 * np.sum((Y - Yunit) ** 2)

作为旁注:以err命名可能会引起误解,因为error通常是成本,而它表示此处每个步骤所取得的进展。行:

    Weights = []
    Weights.append(W)

可以改写为:

   Weights = [W]

J添加到您的Costs列表也是合乎逻辑的,因为这是与W对应的列表:

    Costs = [J]

由于要执行随机梯度下降,因此无需随机选择要从数据集中获取的样本。您有两种选择:要么在每个样本上更新权重,要么可以计算J w.r.t.的梯度。你的体重。后者比前者更易于实现,并且通常会更加融合。但是,由于您选择了前者,因此这是我将要使用的。请注意,即使在此版本中,您也不必随机选择样本,但是我将使用与您相同的方法,因为这也可以工作。关于采样,我认为最好不要两次获取相同的索引。因此,您可能想像这样定义index

    index = np.random.choice(np.arange(len(Y)),size=K,replace=False)

m是无用的,因为在这种情况下,它始终等于K。如果执行采样而没有确保两次没有相同的索引,则应保留该索引。如果要执行采样而不检查是否对同一索引采样了两次,只需将replace=True放在choice函数中即可。

再次,您可以使用矩阵乘法来更有效地计算Yunit。因此,您可以替换:

      Ypredsample = []
      for i in range(len(Xsample)):
        Yunit = 0
        for j in range(vars):
          Yunit = Yunit + X[i,j] * W[j]
        Ypredsample.append(Yunit)

作者:

    Ypredsample = X @ W

类似地,您可以使用numpy函数来计算权重更新。因此,您可以替换:

      for i in range(len(Xsample)):
        for j in range(vars):
          gradJunit = (-1)*(Xsample[i,j]*(Ysample[i] - Ypredsample[i]))
          W[j] = W[j] - alpha*gradJunit

作者:

    W -= alpha * np.sum((Ypredsample - Ysample).reshape(-1,1) * Xsample,axis=0)

像以前一样,可以使用矩阵乘法来计算成本。但是请注意,您应该在整个数据集上计算J。因此,您应该替换:

      Jnew = 0
      for i in range(len(Xsample)):
        Yunit = 0
        for j in range(vars):
          Yunit = Yunit + Xsample[i,j]*W[j]
          Jnew = Jnew + (0.5/(len(Xsample)))*((Ysample[i]-Yunit)**2)

作者:

   Jnew = 0.5 * np.sum((Y - X @ W) ** 2)

最后,您可以使用矩阵乘法进行预测。因此,您的最终代码应如下所示:

import numpy as np

X = np.random.randn(100,5)
Y = X @ np.array([2,1]) + 5

def multilinreg(X,Y,epsilon=0.00001,alpha=0.01,K=20):
    X = np.hstack((np.ones(len(X)).reshape(-1,X))
    vars = X.shape[1]
    W = np.random.normal(size=vars)
    Yunit = X @ W
    J = 0.5 * np.sum((Y - Yunit) ** 2)
    err = 1
    Weights = [W]
    Costs = [J]
    iter = 0

    while err > epsilon:
        index = np.random.choice(np.arange(len(Y)),replace=False)
        Xsample,Ysample = X[index],Y[index]
        Ypredsample = Xsample @ W
        W -= alpha * np.sum((Ypredsample - Ysample).reshape(-1,axis=0)
        Jnew = 0.5 * np.sum((Y - X @ W) ** 2)
        Weights.append(Jnew)
        err = abs(Jnew - J)
        J = Jnew
        Costs.append(J)
        iter += 1

        if iter % 10 == 0:
            print(iter)
            print(J)

    Costs = np.array(Costs)
    Ypred = X @ W
    return Ypred,iter,Costs,W

运行它会在61次迭代中返回W=array([4.99956786,2.00023614,3.00000213,1.00034205,3.99963732,1.00063196]),最终成本为3.05e-05。

现在我们知道此代码是正确的,我们可以使用它来确定您的错误地方。在这段代码中:

      for i in range(len(Xsample)):
        Yunit = 0
        for j in range(vars):
          Yunit = Yunit + X[i,j] * W[j]
        Ypredsample.append(Yunit)
      Ypredsample = np.array(Ypredsample)

您使用X[i,j]而不是Xsample[i,j],这没有任何意义。另外,如果您在循环中同时打印WJiter,则可以看到程序很快找到了正确的W(一旦先前的修复程序被),但不会停止,可能是因为J的计算不正确。错误是该行:

Jnew = Jnew + (0.5/(len(Xsample)))*((Ysample[i]-Yunit)**2)

没有正确缩进。确实,它不应该是for j in range(vars)循环的一部分,而应该仅仅是for i in range(len(Xsample))循环的一部分,像这样:

      Jnew = 0
      for i in range(len(Xsample)):
        Yunit = 0
        for j in range(vars):
          Yunit = Yunit + Xsample[i,j]*W[j]
        Jnew = Jnew + (0.5/(len(Xsample)))*((Ysample[i]-Yunit)**2)

通过更正此问题,您的代码可以正常工作。该错误也出现在程序的开头,但是只要完成两次以上的迭代,就不会影响该错误。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)> insert overwrite table dwd_trade_cart_add_inc > select data.id, > data.user_id, > data.course_id, > date_format(
错误1 hive (edu)> insert into huanhuan values(1,'haoge'); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive> show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 <configuration> <property> <name>yarn.nodemanager.res