李的深度学习二:线性表示

        李哥说这是最基本的需要用到的东西,以后的项目都围绕这些来做。他用代码带我们写了怎样实现线性方程组y=x*w+b这样的算法

import torch
import matplotlib.pyplot as plt #  画图的
import random  #随机

        李哥首先引用了上面三个库,我将torch理解为某一群高手发明的用于矩阵运算的库,plt是一个用于画图的库,random很好理解,是一个随机的库。

def create_data(w, b, data_num):  #生成数据
    x = torch.normal(0, 1, (data_num, len(w)))
    y = torch.matmul(x, w) + b    #matmul表示矩阵相乘

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

    return x, y

        上面是李哥定义的第一个函数create_data(),用于生成数据,其中的x和y就是为了满足y=x*w+b这个运算的真实数,x是一个矩阵,y是x经过矩阵运算后得到的列向量。传入的w和b都是向量,w是权重,用来预测x求出的y,b是偏置,用来提供基础值

x = torch.normal(0, 1, (data_num, len(w)))

        矩阵x的形状由传入的参数data_num确定行,len(w)确定列,在torch.normal()中,前两个参数0和1代表的是正态分布中的均值为0,标准差为1,生成随机数作为矩阵x中的元素。

noise = torch.normal(0, 0.01, y.shape)

        noise是噪声,目的是为了追求普遍适应性,所以要模拟出来再加到y上。0和0.01依然是正态分布中的均值0和标准差0.01,根据向量y的形状y.shape生成元素,加到y上。最后返回矩阵x和真实值y。

num = 500

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

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

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

        这里李哥定义了传入create_data()的参数,num=500说明要做一个500行的矩阵,true_w和true_b分别预测了这个f(x)会输出什么结果。

        plt是画图,绘制散点图函数scatter()中参数X[:,3]的“:”意思是二维数组(矩阵)X的所有行,“3”意思是下标为3实际上是第4列的元素,总体来说就是拿第四列数据为散点图的x轴。Y作为散点图的y轴。1表示点的大小。

def data_provider(data, label, batchsize):       #每次访问这个函数, 就能提供一批数据
    length = len(label)
    indices = list(range(length))
    #我不能按顺序取  把数据打乱
    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  #有存档点的return

batchsize = 16
# for batch_x, batch_y in data_provider(X, Y, batchsize):
#     print(batch_x, batch_y)
#     break

        上面是李哥又定义了一个data_provider()函数,传进来的三个参数分别是数据,标签,步长。目的应该是为了随机抽取这么多数据中的一部分样本,数量根据步长来看是每次取16行的数据。

        length是标签的长度。indices是数据的索引,生成了一个顺序的列表,里面是刚才的num定下的总共500个数据

        random.shuffle()将会把indices这个顺序列表打乱

    for each in range(0, length, batchsize):

        这个循环的意思是从indices这个列表长度的下标0开始,以步长batchsize=16遍历整个indices这个数据集合。这个时候遍历出来的数据虽然下标是按顺序的,但是实际上数据的顺序是被打乱了的。

get_indices = indices[each: each+batchsize]
get_data = data[get_indices]
get_label = label[get_indices]

        yield get_data,get_label  #有存档点的return

         然后把这一次取出的样本存到get_indices中。get_data和get_label就是要让这一份样本的数据和标签能够一一对应。yield是一个与return类似功能的东西,会把当前遍历的这份样本数据先返回出来,在下一次返回的就会是下一批样本的数据了。比如这次返回的是0到15共16个数据,下一次就是返回16到31共16个数据,这样一批一批地输出。

def fun(x, w, b):
    pred_y = torch.matmul(x, w) + b
    return pred_y

        接下来李哥定义了一个功能函数fun(),为了预测本该得出的y是什么样的。和create_data()类似,也是实现了y=x*w+b,但是没有添加噪声noise来模拟真实情况。

def maeLoss(pre_y, y):
    return torch.sum(abs(pre_y-y))/len(y)

        上面是李哥定义了一个计算损失的函数maeLoss(),目的是为了得到模拟出的真实值和预测值的差距。abs()计算了pre_y-y这两个张量的差的绝对值,然后sum()求和,再除以y的长度(len(y)),这样就得到了平均绝对误差。返回。算法思想如下图

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

        sgd()是李哥定义的实现简单《随机》梯度下降的函数。设置torch.no_grad()是为了在更新参数的时候不要再去计算梯度,这样容易出错。

        这个式子中η是学习率,L对θ求偏导以后就是梯度,学习率和梯度相乘,加到参数θº得到θ¹,相当于优化了参数。

        所以函数sgd()的功能是循环遍历传入的paras,定义para,作为参数优化以后得到的结果。每次循环都要把使用了的梯度归零,以保证数据的准确性。

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)

        李哥将学习率lr设定为0.03,w_0是根据均值0,标准差0.01随机出的一个参数,而且规模和真实参数true_w一样,

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

requires_grad=True意思是w_0这个参数是求偏导以后的,同理下面的b_0,是直接设置为0.01了,再求b_0的偏导。

epochs = 50

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))

         然后李哥就设置了一个50次的循环。定义一个data_loss = 0,初始化损失的累计值。里面又一个循环,根据data_provider()函数返回的,随机抽取的一批样本,共16个数据(get_data)和对应的标签(get_label)。这个双层循环的作用是遍历50次,每次遍历一批16个数据。

        首先pred_y是预测值,没有加噪声的,由fun()函数算出来,这是深度学习训练过程中的向前传播。loss是损失值,由marLoss()函数得出。

        loss.backward()是实际上进行了的对参数求偏导的操作,实现反向传播,结果会放在grad中,李哥说这一行代码会把所有求出的loss值加到这些参数上(应该是w_0,b_0)。

        sgd()进行简单随机梯度下降,按照:

        优化后的参数=优化前的参数-学习率*参数的偏导数

        求得优化后的参数。

        data_loss += loss累加损失。随后打印结果,每次打印的结果说明损失值的大小,越接近0,说明模型训练的性能越好。

print("真实的函数值是", true_w, true_b)
print("训练得到的参数值是", w_0, b_0)

        训练结束后输出真实函数值和训练得到的参数值。

        这些训练以后得到的参数,之后都会被梯度计算。

idx = 3
plt.plot(X[:, idx].detach().numpy(), X[:, idx].detach().numpy()*w_0[idx].detach().numpy()+b_0.detach().numpy())
plt.scatter(X[:, idx], Y, 1)
plt.show()

        最后画图。先看plot()里的参数:

1.X[:,idx]代表矩阵X的所有行的第4列(前面定义了idx=3,下标从0到3,实际上是第四个的意思)

2. .detach()。李哥说为了画图和以后要进行的操作,就要把它们从张量网上摘下来,不再计算梯度。大模型说是从当前计算图中分离出这个张量。

3. .numpy()。把这些张量转化为NumPy数组。大模型说是因为matplotlib期望输入的是NumPy数组的结构

4.w_0[idx]是w_0这个参数同时也是向量的第4个元素。

        总体来看依然是线性表达y=x*w+b这个形式在特定特征维度idx上的拟合线,x是特征值X[:, idx].detach().numpy(),y是模型预测值X[:,idx].detach().numpy()*w_0[idx].detach().numpy()+b_0.detach().numpy()。

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

拿矩阵X的第四列(idx=3)元素作为图像的x轴,Y作图像y轴,点大小为1。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值