3.1 线性回归
## 3.1.1 线性回归的基本要素
import mxnet as mx
from mxnet import nd
import numpy as np
from time import time
a = nd.ones(shape = 3)
#返回一个元组,是每一维元素的个数
a.shape
#返回NDArray的维度,即数组总共有多少维
a.ndim
#返回NDArray的元素总个数
a.size
a
output:
[1. 1. 1.]
<NDArray 3 @cpu(0)>
c = np.random.randint(0,10,size = (3,3))
c
c.shape
output:
(3, 3)
3.2 线性回归的从零开始实现
3.2.1 生成数据集
# 3.2 线性回归的从零开始实现
# 仅使用NDArray和autograd来实现一个线性回归训练
# 导入所需包及模块,matplotlib包用来作图,且设置为嵌入显示
%matplotlib inline
from IPython import display
from matplotlib import pyplot as plt
from mxnet import autograd, nd
import random
## 3.2.1 生成数据集
num_inputs = 2 # 特征x个个数
num_examples = 1000 # 训练样本数
true_w = [2,-3.4] # 真实权重
true_b = 4.2 # 偏差
features = nd.random.normal(scale = 1,shape = (num_examples,num_inputs)) # input的特征,比如house' age and size.一共一千个样本。
labels = true_w[0] * features[:,0] + true_w[1] * features[:,1] +true_b # 利用输入,和真实的权重、偏差,算出来的真实输出。即标签
labels += nd.random.normal(scale = 0.01, shape = labels.shape) # 加入均值0,标准差0.01的正态分布噪声,代表数据集内的无意义扰动
#features[0],labels[0]
# 定义显示函数
def use_svg_display():
#使用矢量图显示
display.set_matplotlib_formats('svg')
#定义设置图像窗口大小的函数
def set_figsize(figsize = (3.5,2.5)):
use_svg_display()
#设置图的尺寸
plt.rcParams['figure.figsize'] = figsize
set_figsize()
plt.scatter(features[:,1].asnumpy(), labels.asnumpy(), 1); # 1 设置了点的大小
len(features) # features是1000,len()函数返回的是矩阵 行的数量
features.size # features的总元素个数 2000,1000*2
output:
2000
3.2.2 读取数据集
# batch_size: 使用数据集中一小部分样本对模型参数进行一次反向传播的参数更新
# epoch: 使用完整数据集的全部数据对模型进行一次训练
# iteration: 使用一次batch数据对模型进行一次参数更新的过程
def data_iter(batch_size, features, labels):
numexamples = len(features) # len()函数返回矩阵行的数量
indices = list(range(num_examples)) # 生成数据的索引,并转成list的数据格式
random.shuffle(indices) # random.shuffle(list)是随机读取list的元素
for i in range(0, num_examples, batch_size): # 从头开始读,步长是batch_size,
j = nd.array(indices[i : min(i + batch_size, num_examples)]) # 取索引,最大范围内,from i to i+batch_size,i以batch_size间隔增加
yield features.take(j), labels.take(j) # yield 有next()和return功能
batch_size = 10
for X, y in data_iter(batch_size, features, labels):
print(X, y)
break
output:
[[ 0.12447102 -0.8314222 ]
[-0.03348146 -1.0839146 ]
[ 0.14341475 0.42924684]
[-0.6309946 0.7383012 ]
[ 1.8428191 0.2389849 ]
[ 0.2805346 -1.0916246 ]
[-1.2186161 0.9980531 ]
[ 0.300295 0.73225945]
[ 1.3919212 -0.71245486]
[ 1.0571185 -1.4458729 ]]
<NDArray 10x2 @cpu(0)>
[ 7.2816906 7.8203945 3.040133 0.4300802 7.081006 8.460471
-1.6386472 2.3162487 9.419565 11.234173 ]
<NDArray 10 @cpu(0)>
3.2.3 初始化模型参数
# 权重初始化为均值为0,标准差为0.01的正态随机数,b = 0
w = nd.random.normal(scale = 0.01, shape = (num_inputs, 1))
b = nd.zeros(shape = (1,))
# 先调用attach_grad()函数来申请存储计算的梯度的内存
w.attach_grad()
b.attach_grad()
3.2.4 定义模型
# 3.2.4 定义模型
# 使用dot函数做矩阵乘法,来实现线性回归的矢量计算表达式
def linreg(X,w,b):
return nd.dot(X,w) + b
3.2.5 定义损失函数
# 3.2.5 定义损失函数
# 使用平方损失来定义线性回归的损失函数
# y_hat是预测值,y是真实值。两者形状需要相同
def squared_loss(y_hat, y):
return (y_hat - y.reshape(y_hat.shape)) ** 2 /2
3.2.6 定义优化算法
# 3.2.6 定义优化算法
# sgd函数是小批量随机梯度下降算法。它通过不断迭代模型参数来优化损失函数。该自动求梯度模块计算得来的梯度是一个批量样本的梯度和。将它除以批量
# 大小来得到平均值
# 函数参数说明: params: 是传来的权重和偏差[w,b]; lr: 学习率; batch_size: 一批的大小
def sgd(params, lr, batch_size):
for param in params:
param[:] = param - lr * param.grad / batch_size
3.2.7 训练模型
# 3.2.7 训练模型
lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss
for epoch in range(num_epochs):
for X, y in data_iter(batch_size, features, labels):
with autograd.record():
l = loss(net(X, w, b), y) # l是小批量x和y的损失
l.backward() # 小批量的损失对模型参数求梯度
sgd([w,b], lr, batch_size) # 使用小批量随机梯度下降迭代模型参数
# predict_value = net(features, w, b)
train_l = loss(net(features, w, b), labels) # 计算一次epoch的损失
print('epoch %d, loss %f' %(epoch + 1, train_l.mean().asnumpy()))
output:
epoch 1, loss 0.035066
epoch 2, loss 0.000126
epoch 3, loss 0.000049
真实权重和回归得到的权重
true_w, w
([2, -3.4],
[[ 1.9997247]
[-3.3997307]]
<NDArray 2x1 @cpu(0)>)
真实偏差和回归得到的偏差
true_b, b
(4.2,
[4.1994233]
<NDArray 1 @cpu(0)>)
3.2.8显示回归方程的精度
#显示回归方程的精度
difference = predict_value - labels
index_x = features[:,0].size
index_x = list(range(index_x))
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False #符号为方框,设置为否
plt.plot(index_x, difference.asnumpy())
plt.ylim(ymin = -0.5, ymax = 0.5)
plt.xlabel('样本数')
plt.ylabel('预测值和标签之间的差值')
plt.title('回归结果(从零开始)')
plt.grid()
3.3 线性回归的简洁实现
3.3.1 生成数据集
# features 是训练数据特征,labels是标签, 注释参考3.2.1
from mxnet import autograd, nd
num_inputs = 2
num_examples = 1000
true_w = [2, -3.4]
true_b = 4.2
features = nd.random.normal(scale = 1, shape = (num_examples, num_inputs)) # 标准差为1
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
labels += nd.random.normal(scale = 0.01, shape = labels.shape)
3.3.2 读取数据集
#Gluon 提供了data包来读取数据。并命名为gdata
from mxnet.gluon import data as gdata
batch_size = 10
#将训练数据的特征和标签组合
dataset = gdata.ArrayDataset(features, labels)
#随机读取小批量
data_iter = gdata.DataLoader(dataset, batch_size, shuffle = True)
for X, y in data_iter:
print(X, y)
break
3.3.3 定义模型
from mxnet.gluon import nn
net = nn.Sequential()
net.add(nn.Dense(1))
3.3.4 初始化模型参数
from mxnet import init
net.initialize(init.Normal(sigma = 0.01))
3.3.5 定义损失函数
from mxnet.gluon import loss as gloss
loss = gloss.L2Loss() # 平方损失又称为L2损失
3.3.6 定义优化算法
from mxnet import gluon
trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': 0.03})
3.3.7 训练网络
num_epochs = 3
for epoch in range(1, num_epochs + 1):
for X, y in data_iter:
with autograd.record():
l = loss(net(X), y)
l.backward()
trainer.step(batch_size)
l = loss(net(features), labels)
print('epoch %d, loss:%f' %(epoch, l.mean().asnumpy()))
output:
[[-1.5466934 0.08346469]
[-0.72059405 3.2852967 ]
[ 1.2440904 1.4470519 ]
[ 0.60510755 0.9657431 ]
[-1.5252454 0.60032123]
[ 2.135034 -0.8369003 ]
[ 0.72029805 1.1713772 ]
[-0.6160447 -0.81192523]
[ 1.7233957 1.8441548 ]
[-1.7434046 -0.10986138]]
<NDArray 10x2 @cpu(0)>
[ 0.81940717 -8.422266 1.7826811 2.1231296 -0.9000964 11.305915
1.6684434 5.7248397 1.3790796 1.086322 ]
<NDArray 10 @cpu(0)>
epoch 1, loss:0.033029
epoch 2, loss:0.000115
epoch 3, loss:0.000049
展示模型的训练得到的参数和实际参数
# 从net获取所需要的层,并访问其权重和偏差
dense = net[0]
true_w, dense.weight.data()
output:
([2, -3.4],
[[ 1.9991565 -3.4002488]]
<NDArray 1x2 @cpu(0)>)
true_b, dense.bias.data()
(4.2,
[4.1990976]
<NDArray 1 @cpu(0)>)
3.3.8 显示回归方程的精度
# dense.weight.data()[0,0]
prediction = features[:,0] * dense.weight.data()[0,0] + features[:,1] * dense.weight.data()[0,1] + dense.bias.data()
#显示回归方程的精度
diff = prediction - labels
index_x = features[:,0].size
index_x = list(range(index_x))
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False #符号为方框,设置为否
plt.plot(index_x, diff.asnumpy())
plt.ylim(ymin = -0.5, ymax = 0.5)
plt.xlabel('样本数')
plt.ylabel('预测值和标签之间的差值')
plt.title('回归结果(简洁实现)')
plt.grid()