MXNET深度学习框架-03-采用ndarray和autograd实现线性回归

采用ndarray和autograd实现线性回归

       虽然现在有很多深度学习框架,但它们大多继承了所有的回归/分类任务,我们也只能使用它,无法了解其原理,所以,本文只用ndarray和autograd从0开始实现一个简单的线性回归。

        对于学习机器学习或深度学习的开发者来说,线性回归是最基础最入门的一课内容,本文重点将介绍放在代码部分,原理部分需查阅其余文献。一般地,线性回归的方程可写成:
y = x w + b y=xw+b
       其中, x x 为集合点, w w 为权重矩阵, b b 为偏置量。求线性回归其实就是求 w w b b 的值。并且还要最小化集合点上的平方误差:
i = 1 n ( y t y p ) 2 \displaystyle\sum_{i=1}^{n} (y_t-y_p)^2
       其中, y t y_t 为实际值, y p y_p 为预测值。

线性回归模型训练:

1、要训练一个线性回归,首先需要有一个数据集,这里我们可以自定义一个数据集:
        具体采用此方法来产生一个带有噪声的一系列数据点: y [ i ] = 2 x [ i ] [ 0 ] 3.4 x [ i ] [ 1 ] + 4.2 + n o i s e y[i]=2*x[i][0]-3.4*x[i][1]+4.2+noise
        上式中, x x 有两个维度, i i 表示第 i i 个样本,2和-3.4为权重,4.2表示偏置量 b b n o i s e noise 表示噪声,服从均值为0方差为1的正太分布。

true_w=[2,-3.4]
true_b=4.2

x=nd.random_normal(shape=(num_example,num_input)) # 随机生成的数据点x(1000行2列)
y=true_w[0]*x[:,0]+true_w[1]*x[:,1]+true_b

noise=0.01*nd.random_normal(shape=y.shape)  # 按照公式写出代码
y=y+noise
# print(y.shape)
print(x[:10],y[:10]) # 打印前10组数据进行查看

运行结果:
在这里插入图片描述
2、数据读取

batch_size=10
def data_iter():
    idx=list(range(num_example))  # 产生索引
    random.shuffle(idx)  # 打乱数据
    for i in range(0,num_example,batch_size):
        j=nd.array(idx[i:min(i+batch_size,num_example)])
        yield nd.take(x,j),nd.take(y,j)  # nd.take(x,j),从数据中取出相关索引的数据,并返回

那么现在我们读取第一个随机数据块看看:

for data,label in data_iter(): # 读取第一个batch size看看
    print("batch size:",data,label)
    break

运行结果:
在这里插入图片描述
因为我们有1000个样本,那么我们多少次取完?

n=0
for data,label in data_iter(): # 读取第一个batch size看看
    n+=1
print(n)

运行结果:
在这里插入图片描述
正好100次取完(1000/10)。

3、初始化模型参数
设置完数据样本之后,就能开始定义模型,训练参数了。首先我们定义模型参数:

# 初始化模型参数
w=nd.random_normal(shape=(num_input,1))
b=nd.zeros(1,)
params=[w,b]
# 之后会不断训练更新w和b的值,所以我们要创建梯度来进行求导
for param in params: # 对w和b开辟一个临时空间
    params.attach_grad()

4、定义网络
根据公式: y = x w + b y=xw+b 可得:

def net(X): # 根据公式:f(x)=xw+b
    return nd.dot(X,w)+b

5、定义损失函数

def loss_function(y_true,y_pre): # 常使用平方误差来衡量预测值和真实值之间的差距
    return (y_pre-y_true.reshape(y_pre.shape))**2 # 把真实值与预测值的维度变成一样,否则会广播

6、梯度优化器

def SGD(params,lr):
    for pa in params:
        pa[:]=pa-lr*pa.grad # 参数沿着梯度的反方向走特定距离

7、训练模型

epochs=5
lr=0.001
for e in range(0,epochs):
    total_loss=0
    for data, label in data_iter():  # 读取第一个batch size看看
        with ag.record():
            output=net(data)
            loss=loss_function(label,output)
        loss.backward() # 对loss求导
        SGD(params,lr) # 沿着导数的反方向走是可以把loss变低的
        total_loss+=nd.sum(loss).asscalar()
    print("Epoch: %d,average loss: %f"%(e,total_loss/num_example))

运行结果:
在这里插入图片描述
最后我们打印w,b查看一下更新结果:

print(w,b)

运行结果:
在这里插入图片描述
可以看到,w非常接近[2,-3.4],b非常接近4.2,可以说非常准了,模型效果也非常好。
至此,一个完整的线性回归模型就训练完成啦!

附上所有源码:

import mxnet.ndarray as nd
import mxnet.autograd as ag
import random

num_input=2
num_example=1000

'''---生成数据---'''
true_w=[2,-3.4]
true_b=4.2

x=nd.random_normal(shape=(num_example,num_input)) # 随机生成的数据点x(1000行2列)
y=true_w[0]*x[:,0]+true_w[1]*x[:,1]+true_b

noise=0.01*nd.random_normal(shape=y.shape)  # 按照公式写出代码
y=y+noise
# print(y.shape)
print(x[:10],y[:10]) # 打印前10组数据进行查看

'''---数据读取---'''
# 定义一个函数让它每批次读取数据(batch_size)
batch_size=10
def data_iter():
    idx=list(range(num_example))  # 产生索引
    random.shuffle(idx)  # 打乱数据
    for i in range(0,num_example,batch_size):
        j=nd.array(idx[i:min(i+batch_size,num_example)])
        yield nd.take(x,j),nd.take(y,j)  # nd.take(x,j),从数据中取出相关索引的数据,并返回


for data,label in data_iter(): # 读取第一个batch size看看
    print("batch size:",data,label)
    break
'''---取多少次样本?---'''
# n=0
# for data,label in data_iter(): # 读取第一个batch size看看
#     n+=1
# print(n)

'''--------------------定义模型---------------------'''
# 初始化模型参数
w=nd.random_normal(shape=(num_input,1))
b=nd.zeros(1,)
params=[w,b]
# 之后会不断训练更新w和b的值,所以我们要创建梯度来进行求导
for param in params: # 对w和b开辟一个临时空间
    param.attach_grad()

# 网络定义
def net(X): # 根据公式:f(x)=xw+b
    return nd.dot(X,w)+b

# 损失函数定义
def loss_function(y_true,y_pre): # 常使用平方误差来衡量预测值和真实值之间的差距
    return (y_pre-y_true.reshape(y_pre.shape))**2 # 把真实值与预测值的维度变成一样,否则会广播

# 优化
def SGD(params,lr):
    for pa in params:
        pa[:]=pa-lr*pa.grad # 参数沿着梯度的反方向走特定距离

'''-----训练-----'''
epochs=5
lr=0.001
for e in range(0,epochs):
    total_loss=0
    for data, label in data_iter():  # 读取第一个batch size看看
        with ag.record():
            output=net(data)
            loss=loss_function(label,output)
        loss.backward() # 对loss求导
        SGD(params,lr) # 沿着导数的反方向走是可以把loss变低的
        total_loss+=nd.sum(loss).asscalar()
    print("Epoch: %d,average loss: %f"%(e,total_loss/num_example))
print(w,b)
发布了88 篇原创文章 · 获赞 39 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/Daker_Huang/article/details/104881394