您的位置:首页 > 其它

MXnet实战之线性回归

2017-10-27 16:20 267 查看

线性回归介绍

线性回归模型的目标是找一根线,使得这根线能很好得拟合训练数据并且能够很好得泛化。当使用线性回归模型时,问题应该定义成回归问题,数据集应该处理成(X,Y),其中X是具体情况下每条样本得特征,维度按照实际情况定义,但要保证所有数据维度相同,Y为对应的目标值。特征X每个维度都有个权值,用向量w表示,b表示偏置,用数学符号表示为:

Y^ = Xw + b


训练过程:首先我们随机初始化w和b(其实就是模型要训练得到的参数)。对于训练集,我们输入每个X,都有其对应的真实目标值Y,然后按照Xw + b可以计算出实际值Y^,模型训练的目标就是调整参数w和b,使得对于所有训练集,实际值和真实值越靠近越好。那么如何数学化这个问题呢?我们可以通过下面这个式子:



模型训练的目标就是调整w和b,最小化上式,这个在机器学习里面也叫损失函数或者成本函数(上式为平方损失函数)。

实际上线性回归是最简单的神经网络模型。对于线性回归而⾔,它是⼀个两层神经网络,其中第⼀层是(下图橙⾊点)输⼊,每个节点对应输⼊数据点的⼀个维度,第⼆层是单输出节点(下图绿⾊点),它使⽤函数(f(x) = x)作为激活函数。



从0开始学习实现线性回归

代码如下:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#Author: yuquanle
#2017/10/11
#沐神教程实战之线性回归
#本例子使用人工生成数据集做线性回归

import random
from mxnet import ndarray as nd
from mxnet import autograd

num_inputs = 2
num_examples = 1000
batch_size = 10

# 按照Y[i] = 2 * X[i][0] -3.4 * X[i][1] + 4.2 + noise产生人工数据
true_w = [2, -3.4]
true_b = 4.2

X = nd.random_normal(shape=(num_examples, num_inputs))

Y = true_w[0] * X[:, 0] + true_w[1] * X[:, 1] + true_b
# 生成的Y加入噪声0.01 + nd.random_normal(shape=Y.shape)
Y += 0.01 + nd.random_normal(shape=Y.shape)
#print(X[0], Y[0])

# 数据生成器,每次随机num_examples/batch_size组数据样本,一组样本随机包含batch_size个数据点
def data_iter():
# 产生一个随机索引idx,相当与打乱数据集
idx = list(range(num_examples))
random.shuffle(idx)

# i 从0开始,每隔10个数取一个,比如970,980,990
for i in range(0, num_examples, batch_size):
# 选择idx中下标从i开始到i+batch_size的数组成j
j = nd.array(idx[i:min(i+batch_size,num_examples)])
# take(X, j)表示去X中拿下表为j的数
yield nd.take(X, j), nd.take(Y, j)

# for data, label in data_iter():
#     print(data, label)
#     break

# 初始化模型参数,w随机初始化,b初始化为0
w = nd.random_normal(shape=(num_inputs, 1))
b = nd.zeros((1,))
params = [w, b]

# 后面的训练需要求导来更新这些参数的值,需要提前创建它们的梯度
for param in params:
param.attach_grad()

# 定义线性回归模型:y = Wx + b
# w,b为模型需要学习的参数
def net(X):
return nd.dot(X, w) + b

# 定义模型的损失函数,这里使用常见的平方误差
def square_loss(yhat, y):
# 把y变形为yhat的形状来避免自动广播
return (yhat - y.reshape(yhat.shape)) ** 2

# 定义优化方法
# 大多数模型没有显示解,常用梯度下降来(SGD)求解
def SGD(params, lr):
for param in params:
param[:] = param - lr * param.grad

# 模型的训练
# 训练过程通常需要多次迭代数据,迭代一次为一个epoch。一次迭代里,随机读取固定batch_size个数据点,计算梯度并更新参数

epochs = 5
learning_rate = 0.001
for e in range(epochs):
total_loss = 0
# 每次随机取batch_size个数据点
for data, label in data_iter():
with autograd.record():
output = net(data)
loss = square_loss(output, label)
loss.backward()
# 梯度下降更新参数
SGD(params, learning_rate)

total_loss += nd.sum(loss).asscalar()

print("Epoch %d, average loss: %f" % (e, total_loss/num_examples))

# 训练完成之后,模型学到参数w,b。我们和真实参数比较
print(true_w, w)
print(true_b, b)


实验结果:



线性回归—使用 Gluon

Gluon是一个基于MXnet更高层抽象的包,使用它可以更方便的实现一些模型

使用Gluon实现的线性回归代码如下:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#Author: yuquanle
#2017/10/14
#沐神教程实战之线性回归
#本例子使用人工生成数据集做线性回归,使用高层抽象包gluon实现

import random
from mxnet import ndarray as nd
from mxnet import autograd
from mxnet import gluon
num_inputs = 2
num_examples = 1000

# 按照Y[i] = 2 * X[i][0] -3.4 * X[i][1] + 4.2 + noise产生人工数据
true_w = [2, -3.4]
true_b = 4.2

X = nd.random_normal(shape=(num_examples, num_inputs))

Y = true_w[0] * X[:, 0] + true_w[1] * X[:, 1] + true_b
# 生成的Y加入噪声0.01 + nd.random_normal(shape=Y.shape)
Y += 0.01 + nd.random_normal(shape=Y.shape)
#print(X[0], Y[0])

# 数据生成器,每次随机num_examples/batch_size组数据样本,一组样本随机包含batch_size个数据点
def data_iter():
# 产生一个随机索引idx,相当与打乱数据集
idx = list(range(num_examples))
random.shuffle(idx)

# i 从0开始,每隔10个数取一个,比如970,980,990
for i in range(0, num_examples, batch_size):
# 选择idx中下标从i开始到i+batch_size的数组成j
j = nd.array(idx[i:min(i+batch_size,num_examples)])
# take(X, j)表示去X中拿下表为j的数
yield nd.take(X, j), nd.take(Y, j)

# 定义模型
# gluon提供大量提前定制好的层。线性模型就是使用对应的Dense层。

# 利用Sequential将所有层串起来
net = gluon.nn.Sequential()
# 加入Dense层,Dense层唯一需要定义的参数是输出节点的个数,线性模型为1。
# 注意:这里没说输入节点个数,这个后面真正给数据的时候系统会自动赋值。
net.add(gluon.nn.Dense(1))
# 模型初始化
net.initialize()

# gluon提供的平方误差函数
square_loss = gluon.loss.L2Loss()
# 不需要手动实现SGD,只需创建一个Trainer的实例,并且将模型参数传递给它就行。
trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': 0.1})

# 训练过程基本一致,唯一区别是不再调用SGD而是用trainer.step更新模型
epochs = 5
batch_size = 10
for e in range(epochs):
total_loss = 0
for data, label in data_iter():
with autograd.record():
output = net(data)
loss = square_loss(output, label)
loss.backward()
trainer.step(batch_size)
total_loss += nd.sum(loss).asscalar()

print("Epoch %d, average loss: %f" % (e, total_loss / num_examples))

# 训练完成之后,模型学到参数w,b。我们与真实参数比较
# 先从net拿到需要的层
dense = net[0]
print(true_w, dense.weight.data())
print(true_b, dense.bias.data())


实验结果如下:

Epoch 0, average loss: 1.380673
Epoch 1, average loss: 0.467346
Epoch 2, average loss: 0.463143
Epoch 3, average loss: 0.470569
Epoch 4, average loss: 0.470368
[2, -3.4]
[[ 2.08668685 -3.4323175 ]]
<NDArray 1x2 @cpu(0)>
4.2
[ 4.3376236]
<NDArray 1 @cpu(0)>


我们可以发现,训练求得的参数w和b和真实参数基本一致。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: