01基于MXNet从零实现线性回归

目录

背景知识

深度模型的四大基本要素

1.模型(model)

2.训练数据(training set)

3.损失函数(loss function) --- 平方损失(square loss)

4.优化算法 --- 小批量随机梯度下降(mini-batch stochastic gradient descent)

NDArray数据操作

autograd包求梯度

代码流程

0 导入包或模块

1 生成数据集

2 读取数据

3 初始化模型参数w、b

4 定义模型

5 定义损失函数

6 定义优化算法

7 训练模型

练习

Q1:为什么squared_loss函数中需要使用reshape函数?

Q2:尝试使用不同的学习率,观察损失函数值的下降快慢。

Q3:如果样本个数不能被批量大小整除,data_iter函数的行为会有什么变化?


背景知识

《动手学深度学习》

第3章 深度学习基础

3.1 线性回归

本节以线性回归为例,介绍大多数深度学习模型的基本要素和表示方法。

3.2 线性回归的从零开始实现

只利用NDArray和autograd来实现一个线性回归的训练。

深度模型的四大基本要素

1.模型(model)

模型就是y关于x的表达式,线性回归中y与x之间是线性关系,表达式例:\hat{y} = x_{1}w_{1}+x_{2}w_{2}+b。在代码中定义函数linreg()表达这个模型,输入特征X、权重w,偏差b,输出预测 \hat{y}

def linreg(X, w, b):
    return nd.dot(X, w) + b

2.训练数据(training set)

Q:我们需要构建什么样的数据集?需要设置哪些变量?

A:根据上述模型的形式,我们最起码要定义特征X、权重w、偏差b、标签y这些变量。在代码中,可以这样定义:

样本数:n_samples

特征数:n_features

权重:w,形状(1,n_features)

偏差:b,标量

特征:features,形状(n_samples,n_features)

标签:labels,形状(n_samples,1)

3.损失函数(loss function) --- 平方损失(square loss)

衡量误差的函数称为损失函数。这里使用的平方误差函数也被称为平方损失(square loss)。通常,我们用训练数据集中所有样本误差的平均来衡量模型预测的质量,即:

l(w_{1},w_{2},b) = \frac{1}{n}\sum_{i=1}^{n}l^{(i)}(w_{1},w_{2},b) =\frac{1}{n}\sum_{i=1}^{n}\frac{1}{2}(\hat{y}^{(i)} - y^{(i)} )^{2} 

在代码中我们定义函数squared_loss()去表示损失函数,输入预测\hat{y}、标签y,输出损失l

def squared_loss(y_hat, y):
    return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2 

4.优化算法 --- 小批量随机梯度下降(mini-batch stochastic gradient descent)

小批量随机梯度下降算法:我们先选取一组模型参数的初始值,例如随机选取;接下来对参数进行多次迭代,使得每次迭代都可能降低损失函数的值。在每次迭代中,我们先随机均匀采样一个由固定数目训练数据样本所组成的小批量(mini-batch)B;然后求小批量中数据样本的平均损失有关模型参数的导数(梯度);最后用此结果与预先设定的一个正数的乘积作为模型参数在本次迭代的减小量。

在训练本节讨论的线性回归模型的过程中,模型的每个参数将作如下迭代:

w_{1}\leftarrow w_{1} - \frac{\eta }{\left | B \right |}\sum_{i\in B}^{ } \frac{\partial l^{(i)}(w_{1},w_{2},b)}{\partial w_{1}} = w_{1} - \frac{\eta }{\left | B \right |}\sum_{i\in B}^{ } x_{1}^{(i)}(x_{1}^{(i)}w_{1} +x_{2}^{(i)}w_{2} + b - y^{(i)})

w_{2}\leftarrow w_{2} - \frac{\eta }{\left | B \right |}\sum_{i\in B}^{ } \frac{\partial l^{(i)}(w_{1},w_{2},b)}{\partial w_{2}} = w_{2} - \frac{\eta }{\left | B \right |}\sum_{i\in B}^{ } x_{2}^{(i)}(x_{1}^{(i)}w_{1} +x_{2}^{(i)}w_{2} + b - y^{(i)})

b\leftarrow b - \frac{\eta }{\left | B \right |}\sum_{i\in B}^{ } \frac{\partial l^{(i)}(w_{1},w_{2},b)}{\partial b} = b - \frac{\eta }{\left | B \right |}\sum_{i\in B}^{ } (x_{1}^{(i)}w_{1} +x_{2}^{(i)}w_{2} + b - y^{(i)})

其中超参数(hyperparameter):

\left | B \right |:批量大小(batch size),每个小批量中的样本个数。

\eta:学习率(learning rate),取正数。

我们通常所说的“调参”指的正是调节超参数,例如通过反复试错来找到合适的超参数。

迭代形式简化:优化后的参数w^{*} / b^{*} = 原参数w_{i} / b - 学习率\eta * 这一批量样本的平均损失对此参数的梯度\frac{1}{\left | B \right |}\sum_{i\in B}^{ } \frac{\partial l^{(i)}(w_{1},w_{2},b)}{\partial w_{i}/b},其中一个批量样本的平均损失 = 一个批量样本的损失和 / 批量大小B。(贴合上述原理的理解)

或者可以解释为:优化后的参数w^{*} / b = 原参数w_{i} / b - 学习率\eta * 这一批量样本的损失对此参数的梯度和\sum_{i\in B}^{ } \frac{\partial l^{(i)}(w_{1},w_{2},b)}{\partial w_{i}/b} / 批量大小B。(贴合下述代码的理解,因为下述代码中的param.grad计算出的梯度是一个批量样本的梯度和)

代码中,定义函数sgd()表示优化算法,输入参数params(形式是[w,b])、学习率lr、批量大小batch_size,根据上述公式形式,输出优化后的参数。

def sgd(params, lr, batch_size):
    for param in params:
        param[:] = param - lr * param.grad / batch_size

NDArray数据操作

在MXNet中,NDArray是存储和变换数据的主要工具。NDArray提供GPU计算和自动求梯度等更多功能,这些使得NDArray更加适合深度学习。具体使用参阅 2.2 数据操作 一节。

from mxnet import nd 

nd.arange、nd.zeros、nd.ones、nd.array、nd.random.normal;.shape、.size、.reshape

+、-、*、/、==、nd.dot()、nd.concat()、.exp()、.sum()、.norm().asscalar()

广播、索引

autograd包求梯度

# 在MXNet中,使用autograd包来自动求梯度。
# 一个例子:y = x的平方,求y对x的梯度。

from mxnet import autograd, nd

# 创建变量x
x=nd.arange(4).reshape((4,1))

# 先调用attach_grad函数来申请存储梯度所需要的内存。
x.attach_grad()

# 为了减少计算和内存开销,默认条件下MXNet不会记录用于求梯度的计算。我们需要调用record函数来要求MXNet记录与求梯度有关的计算。
with autograd.record():
    y = 2 * nd.dot(X.T, x)

# 调用backward函数自动求梯度。注意:如果y不是一个标量,MXNet将默认先对y中元素求和得到新的变量,再求该变量有关x的梯度。
y.backward()

# 来验证一下求出来的梯度是正确的。
assert(x.grad - 4 * x).norm().asscalar() == 0
x.grad

代码流程

0 导入包或模块

# 0.导入包或模块
# 作的图嵌入显示
%matplotlib inline 
from IPython import display
from matplotlib import pyplot as plt
from mxnet import autograd, nd
import random

1 生成数据集

前面讲基本要素的第2项里说明了要定义的变量,在此再陈列一下:

样本数:n_samples

特征数:n_features

权重:w,形状(1,n_features)

偏差:b,标量

特征:features,形状(n_samples,n_features)

标签:labels,形状(n_samples,1)

这里我们把标签的形式改动一下,增加一个随机噪声项\varepsilon\varepsilon服从均值为0、标准差为0.01的正态分布,最终标签形式为: y = Xw + b + \varepsilon 。

另外,这里的参数w,b是自己设置的用来生成训练集的参数。我们后续还要初始化一个用于训练模型的参数w,b,这个初始化的参数才是我们后续要训练的参数,训练后的参数会逼近我们现在设置的这两个参数,所以我们称现在设置的这个是真实参数,相当于我们提前知道了优化后的参数大概是什么数值,这也是因为我们在人工设计生成数据集。如果数据集不是我们人工生成的,那其中待训练的参数会训练成什么样我们是不知道的。注意区分这其中的区别。

# 1.生成数据集
n_samples = 1000
n_features = 2
# 用来生成训练集的真实参数
true_w = [2, -3.4]
true_b = 4.2
features = nd.random.normal(scale = 1, shape = (n_samples, n_features))
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
labels += nd.random.normal(scale = 0.01, shape = labels.shape)
# 生成第二个特征features[:, 1]和标签labels的散点图。发现两者呈现相关关系。
def use_svg_display():
    "用矢量图显示。'svg'表示矢量图格式。"
    ## https://ipython.readthedocs.io/en/stable/api/generated/IPython.display.html
    display.set_matplotlib_formats('svg')
    
def set_figsize(figsize = (3.5, 2.5)):
    use_svg_display()
    "设置图像大小"
    ## https://www.cnblogs.com/pacino12134/p/9776882.html
    ## pylot使用rc配置文件来自定义图形的各种默认属性,称之为rc配置或rc参数。
    ## 通过rc参数可以修改默认的属性,包括窗体大小、每英寸的点数、线条宽度、颜色、样式、坐标轴、坐标和网络属性、文本、字体等。
    ## rc参数存储在字典变量中,通过字典的方式进行访问
    ## https://blog.csdn.net/weixin_44675377/article/details/96367050
    plt.rcParams['figure.figsize'] = figsize
    plt.rcParams['lines.markersize'] = 1

set_figsize()
plt.scatter(features[:, 1].asnumpy(), labels.asnumpy())

2 读取数据

我们不是一次训练完所有的数据集,而是分成很多小批量数据样本去训练,因此我们需要一个函数,把训练集样本划分成批量大小为batch_size的若干样本。函数名定义为data_iter(),输入想要设置的批量大小batch_size、特征features、标签labels,输出batch_size个随机样本的特征和标签。

# 2.读取数据
def data_iter(batch_size, features, labels):
    "返回batch_size个随机样本的特征和标签"
    n_samples = len(features)
    # 把所有样本数列成一个列表,为了后续打乱。
    indices = list(range(n_samples))
    # 随机打乱样本。random.shuffle(x, random=None),x是list类型。
    random.shuffle(indices)
    # i表示批量起始的索引,也控制着样本共产生多少个批量
    # j表示从[i,min(i + batch_size, n_samples))的索引
    ## 若batch_size = 10
    ## 第一批:j = [0,min(10,1000)) = [0,10)
    ## 最后一批:j = [990,min(1000,1000)) = [990,1000)
    ## 若batch_size = 9
    ## 第一批:j = [0,min(9,1000)) = [0,9)
    ## 倒数第二批:j = [990,min(999,1000)) = [990,999)
    ## 最后一批:j = [999,min(1008,1000)) = [999,1000) = 999
    for i in range(0, n_samples, batch_size):
        j = nd.array(indices[i: min(i + batch_size, n_samples)])
        # take函数根据索引返回对应元素。
        yield features.take(j), labels.take(j)
batch_size = 10
# 第1次循环,i = 0,j是10维的行向量,表示样本的索引。
# 此时返回的是第一个小批量数据样本。
for X, y in data_iter(batch_size, features, labels):
    print(X, y)
    break

[[-0.11860341  1.4148241 ]
 [ 1.8987794  -1.1283917 ]
 [-0.20462541  0.25932682]
 [-0.4099693   0.6933207 ]
 [ 0.16420342  0.53501797]
 [-0.7698555   1.1850222 ]
 [ 0.53314596 -0.7292533 ]
 [-0.72485566  1.1119636 ]
 [-0.77872294 -1.5016645 ]
 [-0.65356195  0.627186  ]]
<NDArray 10x2 @cpu(0)> 
[-0.8272222  11.833702    2.9046068   1.0253108   2.697544   -1.3880213
  7.741288   -1.0268766   7.7674737   0.77625644]
<NDArray 10 @cpu(0)> 

3 初始化模型参数w、b

这里的w、b才是我们要经过模型训练的参数,如果训练的好,会无限逼近我们先前设置的用于生成训练集的真实参数true_w,true_b。

# 3.初始化模型参数
# w服从均值为0、标准差为0.01的正态分布
w = nd.random.normal(scale = 0.01, shape = (n_features,1))
b = nd.zeros(shape = (1,))
# 申请存储梯度所需的内存
w.attach_grad()
b.attach_grad()

4 定义模型

# 4.定义模型
def linreg(X, w, b):
    return nd.dot(X, w) + b

5 定义损失函数

# 5.定义损失函数
def squared_loss(y_hat, y):
    return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2 

6 定义优化算法

# 6.定义优化算法
def sgd(params, lr, batch_size):
    for param in params:
        param[:] = param - lr * param.grad / batch_size

7 训练模型

在训练之前,我们应先定义两个超参数---学习率(lr) 和批量大小 (batch_size),提示下这里的批量大小在前面定义了。在训练中,我们将多次迭代模型参数,以达到更好的优化效果,所以还需要定义一个迭代周期个数变量(n_epochs)。

在每一个迭代周期(epoch)中,我们根据当前读取的小批量数据样本(特征X和标签y),记录每个小批量的损失 l ,通过调用反向函数backward计算小批量随机梯度,并调用优化算法sgd迭代模型参数。在这一个迭代周期中,我们完整地遍历了一遍data_iter函数,对训练数据集中所有样本都使用一次(假设样本数能够被批量大小整除),使用完所有样本后,我们以优化后的参数代入模型计算出预测值,然后与标签值比较,查看损失情况。

# 7.训练模型
lr = 0.03 #学习率
n_epochs = 3 #迭代周期个数
net = linreg #模型函数
loss = squared_loss #损失函数

for epoch in range(num_epochs):
    # 在每一个迭代周期epoch里,使用所有的训练集样本一次(假设样本数据能被批量大小整除)
    # 调用data_iter()把训练集样本分批次训练。X,y分别是小批量样本的特征和标签。
    for X, y in data_iter(batch_size, features, labels):
        with autograd.record(): # 记录与梯度有关的计算
            l = loss(net(X, w, b), y)  # 记录本批量样本的损失
        l.backward() # 求本批量样本的损失对模型参数的梯度
        sgd([w, b], lr, batch_size) #利用小批量随机梯度下降算法sgd()迭代优化模型参数w、b
    train_l = loss(net(features, w, b), labels)  # 用优化后的参数训练得到的预测值与标签值比较,得到损失train_l
    print("epoch %d, loss %f" % (epoch + 1, train_l.mean().asnumpy()))

epoch 1, loss 0.034846
epoch 2, loss 0.000125
epoch 3, loss 0.000049  

训练完成后,我们可以比较学到的参数和用来生成训练集的真实参数,它们应该很接近。 

true_w, w

([2, -3.4],
 
 [[ 1.9996865]
  [-3.399794 ]]
 <NDArray 2x1 @cpu(0)>) 

true_b, b

(4.2,
 
 [4.199676]
 <NDArray 1 @cpu(0)>) 

练习

Q1:为什么squared_loss函数中需要使用reshape函数?

A1:写代码试验一下,可以看到yy_hat的形状是(1000,1)是二维的NDArray,而yy的形状是(1000,)是一维的NDArray。如果不使用reshape函数,二者相减yy_hat - yy会引发NDArray的广播机制,得到形状是(1000,1000)的结果,不符合我们的需求。我们想要的损失形状是(1000,1),所以必须要使用reshape函数(y.reshape(y_hat.shape))把y从一维(1000,)变为二维(1000,1)。

#def squared_loss(y_hat, y):
#    return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2 
yy_hat = linreg(features, w, b)
yy = labels
print("yy_hat.shape:",yy_hat.shape," yy.shape:",yy.shape)
print("不reshape,(yy_hat - yy).shape:",(yy_hat - yy).shape, (yy - yy_hat).shape)
print("yy.reshape(yy_hat.shape).shape:",yy.reshape(yy_hat.shape).shape)
print("loss function返回的shape:",squared_loss(yy_hat, yy).shape)
yy_hat.shape: (1000, 1)  yy.shape: (1000,)
不reshape,(yy_hat - yy).shape: (1000, 1000) (1000, 1000)
yy.reshape(yy_hat.shape).shape: (1000, 1)
loss function返回的shape: (1000, 1)

Q2:尝试使用不同的学习率,观察损失函数值的下降快慢。

A2:把上述7.训练模型进行改写。首先把原来的代码封装成一个trainModel函数,输入参数增加w和b。在函数外,我们定义了一个一维NDArray变量lrs,通过一个循环每次从lrs中取不同的学习率lr代入函数进行训练,并且每次训练前都要以相同的数值初始化w,b,以保证可以平等观测各个学习率的迭代效果。我们一共设置了9个不同的学习率,并把迭代周期设为10,通过观察运行结果可以发现当lr = 0.001、0.003时收敛速度过慢,在10次迭代中没有收敛到最小值;当lr = 0.01时最终在第10次迭代周期里达到最佳结果,但这是我们根据其他信息得知的,事实上直观看上去它并没有进入一个平稳期,在10次迭代周期里仍属于下降趋势;当lr = 0.03时,可以看到第4次迭代已经收敛,往后迭代得到的值都趋于平稳,我们可以说这是一个比较好的学习率;当lr = 0.1时,值虽然很小,但很明显开始来回震荡,并且可以预测到会一直震荡下去,也不是一个收敛的状态;当lr = 0.3、1时,和前面情况差不多,也是震荡,但明显震荡幅度更大;当lr = 3、10时,学习率都过高,每一步更新都跨度太大,不可控制,那此时的损失是不可计算的,于是返回了nan。综上,学习率不可过小,也不可过大,过小会导致值收敛速度过慢,过大会导致震荡或不可计算,在本例中,最优的学习率是0.03。

# 7.训练模型
def trainModel(batch_size, lr, n_epochs, w, b):
    for epoch in range(n_epochs):
        #n_iter = 0 # 记录第几批次
        # 在每一个迭代周期epoch里,使用所有的训练集样本一次(假设样本数据能被批量大小整除)
        # 调用data_iter()把训练集样本分批次训练。X,y分别是小批量样本的特征和标签。
        for X, y in data_iter(batch_size, features, labels):
            #n_iter += 1
            with autograd.record(): # 记录与梯度有关的计算
                l = squared_loss(linreg(X, w, b), y)  # 记录本批量样本的损失
            l.backward() # 求本批量样本的损失对模型参数的梯度
            sgd([w, b], lr, batch_size) #利用小批量随机梯度下降算法sgd()迭代优化模型参数w、b
            # print("epoch %d, n_iter %d, loss:%f" % (epoch + 1, n_iter, l.mean().asnumpy()))
        train_l = squared_loss(linreg(features, w, b), labels)  # 用优化后的参数训练得到的预测值与标签值比较,得到损失train_l
        print("lr %.3f, epoch %d, loss %f" % (lr, epoch + 1, train_l.mean().asnumpy()))

batch_size = 10
lrs = nd.array([0.001, 0.003, 0.01, 0.03, 0.1, 0.3, 1, 3, 10])
n_epochs = 10

for lr in lrs:
    lr = lr.asscalar()
    w = nd.random.normal(scale = 0.01, shape = (n_features,1))
    b = nd.zeros(shape = (1,))
    w.attach_grad()
    b.attach_grad()    
    trainModel(batch_size, lr, n_epochs, w, b)
    print("\n")

lr 0.001, epoch 1, loss 13.724561

lr 0.001, epoch 2, loss 11.210454

lr 0.001, epoch 3, loss 9.157335

lr 0.001, epoch 4, loss 7.480613

lr 0.001, epoch 5, loss 6.111193

lr 0.001, epoch 6, loss 4.992699

lr 0.001, epoch 7, loss 4.079128

lr 0.001, epoch 8, loss 3.332868

lr 0.001, epoch 9, loss 2.723267

lr 0.001, epoch 10, loss 2.225277

 

lr 0.003, epoch 1, loss 9.201860

lr 0.003, epoch 2, loss 5.012722

lr 0.003, epoch 3, loss 2.731747

lr 0.003, epoch 4, loss 1.489387

lr 0.003, epoch 5, loss 0.812434

lr 0.003, epoch 6, loss 0.443357

lr 0.003, epoch 7, loss 0.242055

lr 0.003, epoch 8, loss 0.132219

lr 0.003, epoch 9, loss 0.072259

lr 0.003, epoch 10, loss 0.039516

 

lr 0.010, epoch 1, loss 2.193368

lr 0.010, epoch 2, loss 0.288395

lr 0.010, epoch 3, loss 0.038154

lr 0.010, epoch 4, loss 0.005110

lr 0.010, epoch 5, loss 0.000720

lr 0.010, epoch 6, loss 0.000139

lr 0.010, epoch 7, loss 0.000061

lr 0.010, epoch 8, loss 0.000050

lr 0.010, epoch 9, loss 0.000049

lr 0.010, epoch 10, loss 0.000048

 

lr 0.030, epoch 1, loss 0.034944

lr 0.030, epoch 2, loss 0.000124

lr 0.030, epoch 3, loss 0.000049

lr 0.030, epoch 4, loss 0.000048

lr 0.030, epoch 5, loss 0.000048

lr 0.030, epoch 6, loss 0.000048

lr 0.030, epoch 7, loss 0.000048

lr 0.030, epoch 8, loss 0.000048

lr 0.030, epoch 9, loss 0.000048

lr 0.030, epoch 10, loss 0.000048

 

lr 0.100, epoch 1, loss 0.000050

lr 0.100, epoch 2, loss 0.000048

lr 0.100, epoch 3, loss 0.000049

lr 0.100, epoch 4, loss 0.000049

lr 0.100, epoch 5, loss 0.000049

lr 0.100, epoch 6, loss 0.000048

lr 0.100, epoch 7, loss 0.000048

lr 0.100, epoch 8, loss 0.000049

lr 0.100, epoch 9, loss 0.000049

lr 0.100, epoch 10, loss 0.000049

 

lr 0.300, epoch 1, loss 0.000055

lr 0.300, epoch 2, loss 0.000053

lr 0.300, epoch 3, loss 0.000049

lr 0.300, epoch 4, loss 0.000049

lr 0.300, epoch 5, loss 0.000055

lr 0.300, epoch 6, loss 0.000054

lr 0.300, epoch 7, loss 0.000057

lr 0.300, epoch 8, loss 0.000052

lr 0.300, epoch 9, loss 0.000049

lr 0.300, epoch 10, loss 0.000053

 

lr 1.000, epoch 1, loss 0.000060

lr 1.000, epoch 2, loss 0.000062

lr 1.000, epoch 3, loss 0.000073

lr 1.000, epoch 4, loss 0.000078

lr 1.000, epoch 5, loss 0.000079

lr 1.000, epoch 6, loss 0.000061

lr 1.000, epoch 7, loss 0.000063

lr 1.000, epoch 8, loss 0.000108

lr 1.000, epoch 9, loss 0.000050

lr 1.000, epoch 10, loss 0.000053

 

lr 3.000, epoch 1, loss nan

lr 3.000, epoch 2, loss nan

lr 3.000, epoch 3, loss nan

lr 3.000, epoch 4, loss nan

lr 3.000, epoch 5, loss nan

lr 3.000, epoch 6, loss nan

lr 3.000, epoch 7, loss nan

lr 3.000, epoch 8, loss nan

lr 3.000, epoch 9, loss nan

lr 3.000, epoch 10, loss nan

 

lr 10.000, epoch 1, loss nan

lr 10.000, epoch 2, loss nan

lr 10.000, epoch 3, loss nan

lr 10.000, epoch 4, loss nan

lr 10.000, epoch 5, loss nan

lr 10.000, epoch 6, loss nan

lr 10.000, epoch 7, loss nan

lr 10.000, epoch 8, loss nan

lr 10.000, epoch 9, loss nan

lr 10.000, epoch 10, loss nan

Q3:如果样本个数不能被批量大小整除,data_iter函数的行为会有什么变化?

A3:其实我在2.读取数据里,用代码注释解释了i,j的动作。做个试验,样本数有1000个,我的batch_size设置为9,那分成了1000 / 9 = 111 ,111 + 1 = 112个批量,其余批量都是有9个样本,但最后一个批量只有一个样本。我们看看最后两批量的打印结果,倒数第二批还是特征9x2,9个标签的样式,但最后一批就一个样本,特征是1x2,1个标签。那具体的data_iter函数行为可以print(i) print(j)观察i、j的具体数值,结合我在2.读取数据里的代码注释即可明白。

batch_size = 9
for X, y in data_iter(batch_size, features, labels):
    print(X, y)

最后2批量的运行结果: 

[[-0.25344443  0.35584167]

 [ 0.8647537   0.13802782]

 [ 0.25984043  0.890154  ]

 [-0.30739012 -1.126209  ]

 [ 0.44746986 -0.4954369 ]

 [ 1.7915658  -0.05009377]

 [ 2.3685591  -0.7847614 ]

 [-1.2302123  -1.1133661 ]

 [-0.72800314  0.16671401]]

<NDArray 9x2 @cpu(0)>

[ 2.4974496  5.450164   1.6905847  7.415735   6.783344   7.9528575

 11.622317   5.531663   2.1735208]

<NDArray 9 @cpu(0)>

 

[[-0.31362438  1.3449485 ]]

<NDArray 1x2 @cpu(0)>

[-1.0129491]

<NDArray 1 @cpu(0)>

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值