线性回归代码linear的简单实现

上两节我们学习了理论,接下来让我们基于python来实现一个简单的线性回归,深度学习必须要动手才能理解!

就以第一节里面的所提的简单例子为例来实现

别忘了我们神经网络的目的是:通过随机生成的参数反求真实的参数。

我们往往有很多组输入数据x,而一组输入数据x=[x1,x2,x3,x4]就要与其对应的系数w1,w2,w3,w4相乘,最后加上偏置值b得到一个输出结果y^

又我们有多组输入,那么转为矩阵相乘的形式更直观:

其中一行就代表一组输入

由浅入深,先测试一组数据,看外貌与恋爱次数的关系,假设我们初始设置的参数W与参数B为:(注意这个是真实参数)

true_w = torch.tensor([8.1,2,2,4])
true_b = torch.tensor(1.1)

其他先不用看,盯住8.1和1.1,x1为外貌,则y^=w1*x1+b

我们要模拟这个过程需要三种python包,分别为:

import torch
import matplotlib.pyplot as plt  #画图包

import random

torch是我们的深度学习框架

根据我们上面给出的矩阵乘法图得,共有500组数据,则X为500x4的矩阵,W为4x1的向量,B为500x1的向量(这里需要有线性代数的基本知识)

模拟输入和输出的数据,定义一个函数:

def create_data(w,b,data_num):
    #torch.normal(mean,std,size) size是维度,包含两个数,具体形式为(长,宽)
    x = torch.normal(0,1,(data_num,len(w)))
    y = torch.matmul(x,w)+b   #matmul表示矩阵乘法

#噪声加到y上
    noise = torch.normal(0,0.01,y.shape)
    y += noise
    return x,y

具体的函数功能看我写的注释,值得一提的是模拟的过程必须要加噪声,因为我们实际问题中是一定有噪声的。

normal(期望,方差,维度)是正态分布,维度刚好是500,4即500x4的矩阵捏

随机生成了输入输出数据后,我们用两个变量接受返回值,并给他画出来:

X,Y = create_data(true_w,true_b,num)

plt.scatter(X[:,0],Y,1)
plt.show()

结果:

可以看见颜值对恋爱次数的影响基本呈现线性相关,那我们换一个输入类型(性格)试试呢?

修改成

plt.scatter(X[:,1],Y,1) #X是500x4矩阵,Y是500x1向量,只能让X,Y同形才能画图

结果:

结论:清晰可见系数w最小影响程度越小

有了数据接下来就要模拟整个线性回归的过程了

通过前面两节的学习我们知道,要想通过原数据推测出参数。

一丶首先要建立模型计算得出预测值y^

二丶再设置loss损失函数计算y^与真实数据y的绝对值。

三丶计算得出的损失值L是关于待测两个参数函数,通过将L对w和b求偏导进行梯度回转,找到某一个w和b使得L最小,L越小表示算得的参数越好。

经过不断地迭代,计算出的参数不断逼近真实的参数值。

但是在实际问题中原数据量可能是千千万万的,数量极其庞大,因此我们需要将原数据分批次的进行输入。这才是真的第一步

我们再次把这个图搬出来

初始有500组输入,设置一变量num=500

num = 500

接下来分批处理数据,定义一个函数provide

#每次访问这个函数,就提供一批数据
def data_provider(data,label,batchsize):
    length = len(label) #获取数据集的长度
    indices = list(range(length)) #500变成范围,再化为列表
    #以批次获取数据,批次大小为batchsize
    #随机打乱数据(符合实际问题)
    random.shuffle(indices)

    for each in range(0,length,batchsize):
        get_indices = indices[each:each+batchsize]
        get_data = data[get_indices]
        get_label = label[get_indices]

        yield get_data,get_label #yield返回一个迭代器,就是有存档点的return,每轮循环返回一次不结束函数

#以16个数据为一批次
batchsize = 16

indices是一个列表,存着500个数据的索引值,即0,1,2,3...499

有了这个函数后,我们开始建立模型进行模拟啦!

#定义模型
def fun(x,w,b):
    pred_y = torch.matmul(x,w)+b
    return pred_y

自己定义的模型函数就是原来的线性函数
返回的是y^,也就是预测值

定义损失函数

#定义损失函数
def maeLoss(pre_y,y):
    return torch.sum(abs(pre_y-y))/len(y)

损失函数参数为预测值y^和真实数据y,返回的是一组数据的的平均损失值

计算出损失值L后,进行梯度回卷,pytorch自动计算最小值

#随机梯度下降,更新参数
def sgd(paras,lr):
    with torch.no_grad(): #属于这句代码的部分不需要计算梯度
        for para in paras:
            para -= lr*para.grad  #不能写成para = para-lr*para.grad
            para.grad.zero_()  #使用过的梯度需要归零

注意:每一次梯度计算后需清零,在张量上的梯度是一直值不变的。

好现在开始迭代,我们初始设置迭代次数为50

#训练
epochs = 50

设置学习率和迭代初值

lr = 0.03 #学习率
w_0 = torch.normal(0,0.01,true_w.shape,requires_grad=True) #这个w需要计算梯度
b_0 = torch.tensor(0.01,requires_grad=True)
print(w_0,b_0)

进行共计50轮的梯度回转,注意是每轮500个数据,数据总量还是很多的哈

for epoch in range(epochs):
    data_loss = 0
    for batch_x,batch_y in data_provider(X,Y,batchsize):
        pred_y = fun(batch_x,w_0,b_0)
        loss = maeLoss(pred_y,batch_y)
        loss.backward()#梯度回传
        sgd([w_0,b_0],lr)
        data_loss += loss
    print("epoch %03d:loss:%.6f"%(epoch,data_loss))

OK,训练完成,看看最终输出的参数成果和图:

效果非常好!

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐