第二节:神经网络基础代码

本文详细介绍了使用PyTorch实现数据生成、数据提供函数以及一个简单的线性回归模型的训练过程,包括全连接层的预测、MAELoss计算、梯度下降算法,并展示了模型参数的变化和训练结果可视化。
摘要由CSDN通过智能技术生成

1.数据生成

def create_data(w, b, data_num):
    x = torch.normal(0, 1, (data_num,len(w)))
    y = torch.matmul(x, w) + b

    noise = torch.normal(0,0.01,y.shape)  # 与Y同型
    y += noise # y = y+ noise

    return x, y

输入w,b为两个张量,data_num为int类型,也是x的行数。

torch.normal是按照正态分布生成的随机数,其参数为(EX, DX, (row, col)),如上述代码,生成一个按照均值为0, 方差为1的标准正态分布生成一个随机的data_num*len(w)的矩阵。

torch.matmul用于计算矩阵乘法。使用时需要注意两个向量(或矩阵)的维度是否符合。

noise表示噪声,实际情况必然会存在干扰,取一个方差小的数模拟现场噪声。其中,y.shape表示噪声的分布与y一致,如y为一个列向量的,则noise同上。

最终得到矩阵x与向量y,结果如下:

tensor([[ 0.0488,  0.5074,  1.3997,  1.4776],
        [-0.6572, -0.9599,  1.3429,  0.9530],
        [ 1.3438, -0.9554, -1.4330,  0.1978],
        [ 0.5174, -0.4910,  0.4167, -0.6533],
        [ 0.0413,  1.2715, -0.2776,  0.7668]]) tensor([11.2242,  0.3532,  8.0114,  2.5337,  6.4912])
tensor([[ 0.5174, -0.4910,  0.4167, -0.6533],
        [ 0.0488,  0.5074,  1.3997,  1.4776],
        [-0.6572, -0.9599,  1.3429,  0.9530],
        [ 0.0413,  1.2715, -0.2776,  0.7668],
        [ 1.3438, -0.9554, -1.4330,  0.1978]]) tensor([ 2.5337, 11.2242,  0.3532,  6.4912,  8.0114])

2.数据提供

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

data与label为两个张量,不妨设为上一步所得到的X, Y,batchsize则为一批数据的个数。如果num=500或更大,若一个一个训练则面临内存紧缺的风险。采用batchsize一批一批处理,即使有异常值,也不会对结果造成过大的偏差。

shuffle()函数打乱了indices的顺序。

yield可以理解为有存档点的return。如上例,每调用一次data_provider,就返回两个数据,这两个数据是继承之前结果的:第一次调用返回内容对应的下标为0-15,第二次则为16-31,以此类推。

每batchsize个数据分为一批,故get_data为batchsize行的矩阵,get_label为batchsize行的向量。

根据之前生成的X,Y,由data_provider所提供的数据如下:

batchsize = 16
for batch_x, batch_y in data_provider(X, Y, batchsize):
    print(batch_x)
    print('----------------------------------')
    print(batch_y)
    print('---------------一轮完毕-------------')
tensor([[-0.6969,  0.5601, -1.0732, -0.9905],
        [ 1.0412, -1.3832,  1.0866,  1.5020],
        [ 0.2739, -1.6060,  1.6882,  1.1389],
        [-0.1840,  0.6515,  0.4144, -0.9390],
        [-1.7548,  0.7327, -0.1830, -2.0444],
        [ 0.1011,  0.4585,  0.4756, -0.2226],
        [ 0.3497,  1.3155,  2.8315,  0.6365],
        [ 0.6119, -0.1277, -0.5672,  0.4576],
        [-0.8589, -0.1322, -0.9338, -1.6677],
        [-1.5129, -1.2890, -0.1689,  1.5021],
        [ 0.0945,  1.3924,  0.5321,  1.3429],
        [-0.1838,  0.8477, -1.4569,  0.4252],
        [ 0.3052,  1.1281, -2.0296, -0.1155],
        [-0.6086,  1.1425,  0.4968, -0.1325],
        [ 0.9622, -0.2039,  2.4217, -0.1469],
        [-0.5701,  1.2485,  1.1627,  0.2082]])
----------------------------------
tensor([ -9.5497,  14.9488,   8.0482,  -2.0173, -20.1968,   2.8986,  14.7851,
          6.5136, -14.6656,  -8.0559,  11.0894,   0.1062,   1.3021,  -1.0893,
         12.7336,   2.1376])
---------------一轮完毕-------------

3.过程函数

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


def maeLoss(pred_y, y):
    loss = torch.sum(abs(pred_y - y)) / len(y)
    return loss


def sgd(paras, lr):
    with torch.no_grad():    # 接下来的计算 不算梯度
        for para in paras:
            para -= para.grad*lr        # A -= 3  A = A-3
            para.grad.zero_()      # 把梯度归0

a.预测函数

由第一节内容可得,\hat{y}=b+\sum w_{j}x_{j}。此处x,w,b均为向量或矩阵(张量形式),一层全连接层的pred_y可视为上述计算过程。

b.Loss计算

Loss的输入为预测值pred_y与真实值y。对二者差值的模求和并取平均值,可得到目标loss值。

c.梯度下降

paras表示所有参数,lr(learning rate)表示学习率,para.grad表示这个参数的梯度。

前向过程需要计算梯度,而梯度下降过程即反向回传时不能计算梯度,故需要使用torch,no_grad()。根据梯度下降算法,每一次减去学习率与梯度的积。

para.grad_zero()表示梯度归零。梯度清零是为了避免梯度累加导致训练效果不佳。分批计算使得每次只使用一个 batch 的数据进行反向传播来计算梯度并更新模型参数。如果不清零梯度,每次计算的梯度都会累加在一起,导致模型的更新不准确。

(注意:必须使用para -= para.grad*lr,若写成para = para - para.grad * r 则会被认为创建了一个para)

4.训练

lr = 0.03
w_0 = torch.normal(0, 0.01, true_w.shape, requires_grad=True)  # 计算梯度
b_0 = torch.tensor(0.01, requires_grad=True)  # 计算梯度
print(w_0, b_0)
epochs = 50

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

print("原来的函数值:",true_w, true_b)
print("当前值", w_0, b_0)

基于上述函数,我们可以模拟训练过程。

首先,设置一个学习率lr,确定训练轮数epochs。初始化w_0, b_0,w_0为一个服从正态分布的向量,b_0为一个数值。requires_grad=True表示梯度会在前向过程中保留下来,允许backward()追踪这个参数并求梯度。

接下来可以进行多轮训练。每一轮更新data_loss为0并开始累加,data_provider提供两个张量,故用batch_x(训练集输入参数-矩阵形式), batch_y(训练集真实值y)来接收。backward()表示梯度回传,对每一个全连接层的参数进行梯度下降算法,并用data_loss记录这一批数据的loss值。

最后,对处理前后的值打印并比较,打印结果如下。

epoch 000: loss: 7.677499
epoch 000: loss: 17.258682
epoch 000: loss: 25.320398
epoch 000: loss: 32.237797
epoch 001: loss: 9.790411
epoch 001: loss: 17.207645
epoch 001: loss: 24.195601
epoch 001: loss: 37.130936
epoch 002: loss: 7.068033
epoch 002: loss: 15.839411
epoch 002: loss: 25.096804
epoch 002: loss: 28.144030
epoch 003: loss: 8.310600
epoch 003: loss: 15.232446
epoch 003: loss: 24.112923
epoch 003: loss: 32.670464
epoch 004: loss: 4.021797
epoch 004: loss: 14.452908
epoch 004: loss: 24.347878
epoch 004: loss: 28.234314
......
原来的函数值: tensor([8.1000, 2.0000, 2.0000, 4.0000]) tensor(1.1000)
当前值 tensor([3.7664, 1.5355, 1.6656, 2.8454], requires_grad=True) tensor(1.1762, requires_grad=True)

5.画图

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

  • 36
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值