线性回归的从0开始实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LSC_333/article/details/81211508

利用mxnet的autograd和ndarry来实现线性回归的训练

所需要导入的包和模块:

import random
from IPython.display import set_matplotlib_formats
from matplotlib import pyplot as plt
from mxnet import autograd, nd

 

生成数据

首先我们先生成数据集,假设数据集的样本数是1000,输入是2,则随机生成的批量样本特征 X∈ℝ1000×2

然后我们用公式:y=Xw+b+ϵ来生成标签,这里我设定线性回归模型真实权重 w=[2,−3.4]和偏差 b=4.2,ϵ是一个随机噪声项设定服从均值为0和标准差为0.01的正态分布

num_inputs = 2  #输入是2
num_examples = 1000  #样本数量
true_w = [2, -3.4]   #真实的w
true_b = 4.2  #真实的b
features = nd.random.normal(scale=1, shape=(num_examples, num_inputs))
#样本特征服从均值为0,标准差为1的正态分布,是一个1000*2的ndarry

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的正态分布和标签的shape相同

现在我们可以画出特征值与标签的散点图:

打印出散点图只是为了直观地看出特征值和标签的关系

def set_figsize(figsize=(3.5, 2.5)):
    set_matplotlib_formats('retina')  # 打印高清图。
    plt.rcParams['figure.figsize'] = (3.5, 2.5)  # 设置图的尺寸。
set_figsize()
plt.scatter(features[:, 1].asnumpy(), labels.asnumpy(), 1)
plt.show()
#打印出特征和标签的散点图

这是效果图

读取数据

每次只读取batch_size个数据样本

batch_size = 10  # 每次读取的数据集大小
def data_iter(batch_size, features, labels):
    num_examples = len(features)
    indices = list(range(num_examples))
    # 生成索引
    random.shuffle(indices)
    # 样本的读取顺序是随机的。
    for i in range(0, num_examples, batch_size):
        j = nd.array(indices[i: min(i + batch_size, num_examples)])
        # take 函数根据索引返回对应元素。
        yield features.take(j), labels.take(j)

初始化模型参数

w = nd.random.normal(scale=0.01, shape=(num_inputs, 1))
# w服从均值为0,标准差为0.01的正态分布

b = nd.zeros(shape=(1,))
# b初始化为0

params = [w, b]
for param in params:  # 申请存储梯度所需要的内存
    param.attach_grad()

定义模型

线性回归的矢量计算表达式的实现,使用dot函数做矩阵乘法:

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

定义损失函数

这里我使用平方损失来定义线性回归的损失函数

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

定义优化算法

小批量随机梯度下降算法,通过不断更新模型参数来优化损失函数

这里的lr是学习率

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

训练模型​​​​​​​

训练时的迭代次数可以自己定义,由于收敛的比较快,所以10次就足够了

lr = 0.03  #学习率
num_epochs = 10  # 迭代次数
net = linreg
loss = squared_loss
# 训练模型一共需要 num_epochs 个迭代周期。
for epoch in range(1, num_epochs + 1):
    # 在一个迭代周期中,使用训练数据集中所有样本一次(假设样本数能够被批量大小整除)。
    # X 和 y 分别是小批量样本的特征和标签。
    for X, y in data_iter(batch_size, features, labels):
        with autograd.record():
            # l 是有关小批量 X 和 y 的损失。
            l = loss(net(X, w, b), y)
        # 小批量的损失对模型参数求导。
        l.backward()
        # 使用小批量随机梯度下降迭代模型参数。
        sgd([w, b], lr, batch_size)
    print('epoch %d, loss %f' % (epoch, loss(net(features, w, b), labels).mean().asnumpy()))

训练完成之后,查看训练得到的参数和生成训练集的真实参数:

print("true_w:{}, w:{}" .format(true_w, w))
print("true_b:{}, b:{}" .format(true_b, b))

我得到的结果是:

可以看到还是比较接近真实值的。。。

小结

用pip下载包的时候有时候会特别慢,可以考虑使用国内的镜像比如清华,ustc等等。。。

然后python路径最好是全英文,要不然可能会导致一些奇怪的问题

认真阅读代码,有问题的地方可以试着输出看看

线代+高数+概率论一定要学好。。。

附上完整的python代码:

import random
from IPython.display import set_matplotlib_formats
from matplotlib import pyplot as plt
from mxnet import autograd, nd

num_inputs = 2  #输入是2
num_examples = 1000  #样本数量
true_w = [2, -3.4]   #真实的w
true_b = 4.2  #真实的b
features = nd.random.normal(scale=1, shape=(num_examples, num_inputs))
# 样本特征服从均值为0,标准差为1的正态分布,是一个1000*2的ndarry

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的正态分布和标签的shape相同

def set_figsize(figsize=(3.5, 2.5)):
    set_matplotlib_formats('retina')  # 打印高清图。
    plt.rcParams['figure.figsize'] = (3.5, 2.5)  # 设置图的尺寸。
set_figsize()
plt.scatter(features[:, 1].asnumpy(), labels.asnumpy(), 1)
plt.show()
# 打印出特征和标签的散点图

batch_size = 10  # 每次读取的数据集大小
def data_iter(batch_size, features, labels):
    num_examples = len(features)
    indices = list(range(num_examples))
    # 生成索引
    random.shuffle(indices)
    # 样本的读取顺序是随机的。
    for i in range(0, num_examples, batch_size):
        j = nd.array(indices[i: min(i + batch_size, num_examples)])
        # take 函数根据索引返回对应元素。
        yield features.take(j), labels.take(j)

w = nd.random.normal(scale=0.01, shape=(num_inputs, 1))
# w服从均值为0,标准差为0.01的正态分布

b = nd.zeros(shape=(1,))
# b初始化为0

params = [w, b]
for param in params:  # 申请存储梯度所需要的内存
    param.attach_grad()

def linreg(X, w, b):
    return nd.dot(X, w) + b
def squared_loss(y_hat, y):
    return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2
def sgd(params, lr, batch_size):
    for param in params:
        param[:] = param - lr * param.grad / batch_size

lr = 0.03  #学习率
num_epochs = 10  # 迭代次数
net = linreg
loss = squared_loss
# 训练模型一共需要 num_epochs 个迭代周期。
for epoch in range(1, num_epochs + 1):
    # 在一个迭代周期中,使用训练数据集中所有样本一次(假设样本数能够被批量大小整除)。
    # X 和 y 分别是小批量样本的特征和标签。
    for X, y in data_iter(batch_size, features, labels):
        with autograd.record():
            # l 是有关小批量 X 和 y 的损失。
            l = loss(net(X, w, b), y)
        # 小批量的损失对模型参数求导。
        l.backward()
        # 使用小批量随机梯度下降迭代模型参数。
        sgd([w, b], lr, batch_size)
    print('epoch %d, loss %f' % (epoch, loss(net(features, w, b), labels).mean().asnumpy()))

print("true_w:{}, w:{}" .format(true_w, w))
print("true_b:{}, b:{}" .format(true_b, b))

猜你喜欢

转载自blog.csdn.net/LSC_333/article/details/81211508
今日推荐