深度学习小白的学习笔记(3)——线性回归模型的从零实现(含代码)

这两天学了线性回归模型。线性回归模型对于我们以后的机器学习十分重要,虽然简单,但是其中涉及到的权重(weight)和偏置(bias)的思想会贯穿整个深度学习。

第一步

%matplotlib inline
# 这一行的代码是为了画图的时候可以直接显示在下面的输出框里,不需要再弹出一个新窗口
import random
# 用来随机梯度下降和生成随机的weight
import torch
from d2l import torch as d2l
# d2l包需要自己pip install一下
import os
import numpy
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'

首先我们先导入我们几个所需要用到的库,这里有几个地方需要注意以下:

  1. "%matplotlib inline"这行代码的意思是让我们画的图可以直接在jupyter的交互窗口中显现出来,因为我jupter截的图不太清楚所以我把代码都复制到了pycharm上。
  2. d2l这个包需要自己安装,如果是在pycharm上运行就直接在terminal里面 pip install d2l,在jupyter里面运行的话也是需要pip安装一下
  3. os.environ[‘KMP_DUPLICATE_LIB_OK’] = 'TRUE’这行代码是防止我们在画图的时候图形界面不显示(在pycharm上运行时需要加上这行代码)。

第二步

def synthetic_data(w, b, num_examples):
    """生成 y = Xw + b + 噪声"""
    X = torch.normal(0,1,(num_examples, len(w)))
    y = torch.matmul(X, w) + b
    y += torch.normal(0, 0.01, y.shape)
    return X, y.reshape((-1, 1))

true_w = torch.tensor([2, -3.4])
true_b = 4.2
features, labels = synthetic_data(true_w, true_b, 1000)
print('features:',features[0],'\nlabel:',labels[0])

我们生成一个人造的数据集,y = Xw + b。这里讲解一下几个函数的作用:
1.torch.normal(),该函数用来随机生成一组张量(当一组张量是二维的时候可以直接把它看出一个数组),这组张量的前两个参数用来指定这组张量的均值和方差,比如我们用来生成x的那行代码,它的均值就为0,方差就为1。而后面那个括号则用来指定我们这组张量有多少行,有多少列。
2.toch.matmul(),该函数用来计算张量之间的乘法。在这里需要注意的是,我们在倒数第二行给函数赋值的时候,num_examples = 1000,我们的张量x的形状就是(1000,2)的。((1000,2)表示1000行2列,下面同理。)
不知道这里有没有同学会有这样的问题,在线性代数中,(1000,2)的矩阵和(1,2)的矩阵是不可以相乘的,但是为什么这里是可以计算的。因为在pytorch中,张量比较特殊,这个特殊性简单一点描述起来就是说当我们的张量只有一维时,我们应该把它看作一个列向量。

同时在这里我要分享一个自己悟出来的地方,因为我也刚接触线性模型,所以我的潜意识一致认为线性模型的函数式只能是:

Y = Xw + b

而在上面那个式子中,我们的X是(1000,2)的,w也是有两个。我当时就并没有想通我的样本数明明设的是一个标量,但是为什么我要去利用这个标量再去构造一个二维的张量。其实这里举一个例子可以帮助大家理解。
比如说我们去买房,那影响房子价格的因素肯定非常多,比如这个房子装潢的怎么样,这个小区的环境怎么样,这个房子的地段怎么样,而这里的每一个因素其实就对应了我们的一个X,所以我们可以根据具体的因素条件得到X1,X2…XN。而题目中之所以X是二维的,意思就是假定了我们的影响因素有两个,那么此时我们的线性模型函数也就可以写成:

Y = X1w1 +X2w2 + b

第三步

d2l.set_figsize()
d2l.plt.scatter(features[:,1].detach().numpy(),labels.detach().numpy(),1);

这一步是通过d2l这个包进行作图(d2l这个包是沐神自己写的,写出来就是为了方便画矢量图),作图之后我们可以看到特征和对应标签这件的相关性,这里的plt.scatter()函数是用的matplotlib库里的函数,前两个numpy()数组表示横纵轴,而最后那个参数1表示画出来的点的大小。

第四步

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):
        batch_indices = indices[i:min(i + batch_size, num_examples)]
        yield features[batch_indices], labels[batch_indices]
        
batch_size = 10

for X, y in data_iter(batch_size, features, labels):
    print(X, '\n', y)
    break

第四步简单来说就是从我们人工生成的数据集中随机取样。首先我们的样本数就等于我们的features的长度,接着我们形成一个长度为样本数的列表,并将其命名为索引。因为我们要随机取样,所以我们用random.shuffle()函数将样本打乱。同时,我们这里设置一个batch_size参数,这个就是说我们从打乱的数据中,每次取完一个大小为batch_size的数据块之后,隔一个batch_size的大小再去取样,这样能让我们的样本更具有随机性。这里的min函数的作用是为了防止batch_size间隔取地太大,超出了列表的索引。这里的yield函数可以理解为return,只不过return的这个值是一个可迭代的对象,所以我们可以用for循环对这个对象进行操作。

第五步

定义初始化模型
w = torch.normal(0, 0.01, size=(2, 1), requires_grad = True)
b = torch.zeros(1, requires_grad = True)

定义训练模型
def linreg(X, w, b):
    """线性回归模型"""
    return torch.matmul(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):
    """小批量随机梯度下降"""
    with torch.no_grad():
        for param in params:
            param -= lr * param.grad / batch_size
            param.grad.zero_()

开始训练
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):
        l = loss(net(X, w, b), y)
        #这里的损失都是x 和 y 小批量损失
        l.sum().backward()
        sgd([w, b], lr, batch_size)
    with torch.no_grad():
        train_l = loss(net(features, w, b), labels)
        print(f'epoch{epoch+1}, loss{float(train_l.mean()):f}')

由于接下来的几步只不过就是设定一些常见到的深度学习算法,还有把数据代入进行训练,没有什么参数上理解的困难,所以笔者就直接放到一起了。
希望这篇文章可以帮到你。在深度学习的道路上希望能遇到志同道合的人,一起进步,所以把自己的qq放在这里。qq:793034850。

你可能感兴趣的:(深度学习(pytorch))