
目录一、实验目的二、实验准备三、实验内容1. 线性回归1.1 矢量化加速1.2 正态分布与平方损失2. 线性回归的从零开始实现2.1. 生成数据集2.2 读取数据集2.3. 初始化模型参数2.4. 定义模型2.5. 定义损失函数2.6. 定义优化算法2.7. 训练2.8 小结2.9 练习3. 线性回归的简洁实现3.1. 生成数据集3.2. 读取数据集3.3 定义模型3.4 初始化模型参数3.5 定义损失函数3.6 定义优化算法3.7 训练3.8 小结3.9 练习四、实验心得一、实验目的熟悉PyTorch框架了解PyTorch的基本概念、数据结构和核心函数创建线性回归模型使用PyTorch构建一个简单的线性回归模型该模型能够学习输入特征和目标变量之间的线性关系线性回归从零开始实现及其简洁实现并完成章节后习题。二、实验准备根据GPU安装pytorch版本实现GPU运行实验代码配置环境用来运行 Python、Jupyter Notebook和相关库等相关库。三、实验内容资源获取关注公众号【科创视野】回复 深度学习1. 线性回归1使用jupyter notebook新增的pytorch环境新建ipynb文件完成线性回归从零开始实现的实验代码与练习结果如下1.1 矢量化加速%matplotlib inline import math import time import numpy as np import torch from d2l import torch as d2l n 10000 a torch.ones([n]) b torch.ones([n]) class Timer: #save 记录多次运行时间 def __init__(self): self.times [] self.start() def start(self): 启动计时器 self.tik time.time() def stop(self): 停止计时器并将时间记录在列表中 self.times.append(time.time() - self.tik) return self.times[-1] def avg(self): 返回平均时间 return sum(self.times) / len(self.times) def sum(self): 返回时间总和 return sum(self.times) def cumsum(self): 返回累计时间 return np.array(self.times).cumsum().tolist() c torch.zeros(n) timer Timer() for i in range(n): c[i] a[i] b[i] f{timer.stop():.5f} sec输出结果1.2 正态分布与平方损失def normal(x, mu, sigma): p 1 / math.sqrt(2 * math.pi * sigma**2) return p * np.exp(-0.5 / sigma**2 * (x - mu)**2) # 再次使用numpy进行可视化 x np.arange(-7, 7, 0.01) # 均值和标准差对 params [(0, 1), (0, 2), (3, 1)] d2l.plot(x, [normal(x, mu, sigma) for mu, sigma in params], xlabelx, ylabelp(x), figsize(4.5, 2.5), legend[fmean {mu}, std {sigma} for mu, sigma in params])输出结果2. 线性回归的从零开始实现%matplotlib inline import random import torch from d2l import torch as d2l2.1. 生成数据集def synthetic_data(w, b, num_examples): #save 生成yXwb噪声 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])输出结果d2l.set_figsize() d2l.plt.scatter(features[:, 1].detach().numpy(), labels.detach().numpy(), 1);输出结果2.2 读取数据集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 torch.tensor( 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输出结果2.3. 初始化模型参数w torch.normal(0, 0.01, size(2,1), requires_gradTrue) b torch.zeros(1, requires_gradTrue) w,b输出结果2.4. 定义模型def linreg(X, w, b): #save 线性回归模型 return torch.matmul(X, w) b2.5. 定义损失函数def squared_loss(y_hat, y): #save 均方损失 return (y_hat - y.reshape(y_hat.shape)) ** 2 / 22.6. 定义优化算法def sgd(params, lr, batch_size): #save 小批量随机梯度下降 with torch.no_grad(): for param in params: param - lr * param.grad / batch_size param.grad.zero_()2.7. 训练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形状是(batch_size,1)而不是一个标量。l中的所有元素被加到一起 # 并以此计算关于[w,b]的梯度 l.sum().backward() sgd([w, b], lr, batch_size) # 使用参数的梯度更新参数 with torch.no_grad(): train_l loss(net(features, w, b), labels) print(fepoch {epoch 1}, loss {float(train_l.mean()):f})输出结果print(fw的估计误差: {true_w - w.reshape(true_w.shape)}) print(fb的估计误差: {true_b - b})输出结果2.8 小结我们学习了深度网络是如何实现和优化的。在这一过程中只使用张量和自动微分不需要定义层或复杂的优化器。这一节只触及到了表面知识。在下面的部分中我们将基于刚刚介绍的概念描述其他模型并学习如何更简洁地实现其他模型。2.9 练习1.如果我们将权重初始化为零会发生什么。算法仍然有效吗# 在单层网络中(一层线性回归层)将权重初始化为零时可以的但是网络层数加深后在全连接的情况下 # 在反向传播的时候由于权重的对称性会导致出现隐藏神经元的对称性使得多个隐藏神经元的作用就如同 # 1个神经元算法还是有效的但是效果不大好。参考https://zhuanlan.zhihu.com/p/75879624。2.假设试图为电压和电流的关系建立一个模型。自动微分可以用来学习模型的参数吗# 可以的建立模型UIWb然后采集(UI)的数据集通过自动微分即可学习W和b的参数。 import torch import random from d2l import torch as d2l #生成数据集 def synthetic_data(r, b, num_examples): I torch.normal(0, 1, (num_examples, len(r))) u torch.matmul(I, r) b u torch.normal(0, 0.01, u.shape) # 噪声 return I, u.reshape((-1, 1)) # 标量转换为向量 true_r torch.tensor([20.0]) true_b 0.01 features, labels synthetic_data(true_r, true_b, 1000) #读取数据集 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 torch.tensor(indices[i: min(i batch_size, num_examples)]) yield features[batch_indices],labels[batch_indices] batch_size 10 # 初始化权重 r torch.normal(0,0.01,size ((1,1)), requires_grad True) b torch.zeros(1, requires_grad True) # 定义模型 def linreg(I, r, b): return torch.matmul(I, r) b # 损失函数 def square_loss(u_hat, u): return (u_hat - u.reshape(u_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 10 net linreg loss square_loss for epoch in range(num_epochs): for I, u in data_iter(batch_size, features, labels): l loss(net(I, r, b), u) l.sum().backward() sgd([r, b], lr, batch_size) with torch.no_grad(): train_l loss(net(features, r, b), labels) print(fepoch {epoch 1}, loss {float(train_l.mean()):f}) print(r) print(b) print(fr的估计误差: {true_r - r.reshape(true_r.shape)}) print(fb的估计误差: {true_b - b})输出结果3.能基于普朗克定律使用光谱能量密度来确定物体的温度吗# 3 # 普朗克公式 # x:波长 # T:温度 import torch import random from d2l import torch as d2l #生成数据集 def synthetic_data(x, num_examples): T torch.normal(0, 1, (num_examples, len(x))) u c1 / ((x ** 5) * ((torch.exp(c2 / (x * T))) - 1)); u torch.normal(0, 0.01, u.shape) # 噪声 return T, u.reshape((-1, 1)) # 标量转换为向量 c1 3.7414*10**8 # c1常量 c2 1.43879*10**4 # c2常量 true_x torch.tensor([500.0]) features, labels synthetic_data(true_x, 1000) #读取数据集 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 torch.tensor(indices[i: min(i batch_size, num_examples)]) yield features[batch_indices],labels[batch_indices] batch_size 10 # 初始化权重 x torch.normal(0,0.01,size ((1,1)), requires_grad True) # 定义模型 def planck_formula(T, x): return c1 / ((x ** 5) * ((torch.exp(c2 / (x * T))) - 1)) # 损失函数 def square_loss(u_hat, u): return (u_hat - u.reshape(u_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.001 num_epochs 10 net planck_formula loss square_loss for epoch in range(num_epochs): for T, u in data_iter(batch_size, features, labels): l loss(net(T, x), u) l.sum().backward() sgd([x], lr, batch_size) with torch.no_grad(): train_l loss(net(features, x), labels) print(fepoch {epoch 1}, loss {float(train_l.mean()):f}) print(fr的估计误差: {true_x - x.reshape(true_x.shape)})输出结果4.计算二阶导数时可能会遇到什么问题这些问题可以如何解决# 一阶导数的正向计算图无法直接获得可以通过保存一阶导数的计算图使得可以求二阶导数(create_graph和retain_graph都置为True # 保存原函数和一阶导数的正向计算图)。实验如下 import torch x torch.randn((2), requires_gradTrue) y x**3 dy torch.autograd.grad(y, x, grad_outputstorch.ones(x.shape), retain_graphTrue, create_graphTrue) dy2 torch.autograd.grad(dy, x, grad_outputstorch.ones(x.shape)) dy_ 3*x**2 dy2_ 6*x print() print(dy, dy_) print() print(dy2, dy2_)输出结果5.为什么在squared_loss函数中需要使用reshape函数# 以防y^和y一个是行向量、一个是列向量使用reshape可以确保shape一样。6.尝试使用不同的学习率观察损失函数值下降的快慢。# ①学习率过大前期下降很快但是后面不容易收敛 # ②学习率过小损失函数下降会很慢。7.如果样本个数不能被批量大小整除data_iter函数的行为会有什么变化# 报错。3. 线性回归的简洁实现1使用jupyter notebook新增的pytorch环境新建ipynb文件完成线性回归的简洁实现的实验代码与练习结果如下3.1. 生成数据集import numpy as np import torch from torch.utils import data from d2l import torch as d2l true_w torch.tensor([2, -3.4]) true_b 4.2 features, labels d2l.synthetic_data(true_w, true_b, 1000)3.2. 读取数据集def load_array(data_arrays, batch_size, is_trainTrue): #save 构造一个PyTorch数据迭代器 dataset data.TensorDataset(*data_arrays) return data.DataLoader(dataset, batch_size, shuffleis_train) batch_size 10 data_iter load_array((features, labels), batch_size) next(iter(data_iter))输出结果3.3 定义模型# nn是神经网络的缩写 from torch import nn net nn.Sequential(nn.Linear(2, 1))3.4 初始化模型参数net[0].weight.data.normal_(0, 0.01) net[0].bias.data.fill_(0)输出结果3.5 定义损失函数loss nn.MSELoss()3.6 定义优化算法trainer torch.optim.SGD(net.parameters(), lr0.03)3.7 训练num_epochs 3 for epoch in range(num_epochs): for X, y in data_iter: l loss(net(X) ,y) trainer.zero_grad() l.backward() trainer.step() l loss(net(features), labels) print(fepoch {epoch 1}, loss {l:f})输出结果w net[0].weight.data print(w的估计误差, true_w - w.reshape(true_w.shape)) b net[0].bias.data print(b的估计误差, true_b - b)输出结果3.8 小结我们可以使用PyTorch的高级API更简洁地实现模型。在PyTorch中data模块提供了数据处理工具nn模块定义了大量的神经网络层和常见损失函数。我们可以通过_结尾的方法将参数替换从而初始化参数。3.9 练习1.如果将小批量的总损失替换为小批量损失的平均值需要如何更改学习率# 将学习率除以batchsize。2.查看深度学习框架文档它们提供了哪些损失函数和初始化方法用Huber损失代替原损失即# Huber损失可以用torch自带的函数解决 torch.nn.SmoothL1Loss() # 也可以自己写 import torch.nn as nn import torch.nn.functional as F class HuberLoss(nn.Module): def __init__(self, sigma): super(HuberLoss, self).__init__() self.sigma sigma def forward(self, y, y_hat): if F.l1_loss(y, y_hat) self.sigma: loss F.l1_loss(y, y_hat) - self.sigma/2 else: loss (1/(2*self.sigma))*F.mse_loss(y, y_hat) return loss #%% import numpy as np import torch from torch.utils import data from d2l import torch as d2l #%% true_w torch.tensor([2, -3.4]) true_b 4.2 features, labels d2l.synthetic_data(true_w, true_b, 1000) def load_array(data_arrays, batch_size, is_train True): #save pytorch数据迭代器 dataset data.TensorDataset(*data_arrays) # 把输入的两类数据一一对应*表示对list解开入参 return data.DataLoader(dataset, batch_size, shuffle is_train) # 重新排序 batch_size 10 data_iter load_array((features, labels), batch_size) # 和手动实现中data_iter使用方法相同 #%% # 构造迭代器并验证data_iter的效果 next(iter(data_iter)) # 获得第一个batch的数据 #%% 定义模型 from torch import nn net nn.Sequential(nn.Linear(2, 1)) # Linear中两个参数一个表示输入形状一个表示输出形状 # sequential相当于一个存放各层数据的list单层时也可以只用Linear #%% 初始化模型参数 # 使用net[0]选择神经网络中的第一层 net[0].weight.data.normal_(0, 0.01) # 正态分布 net[0].bias.data.fill_(0) #%% 定义损失函数 loss torch.nn.HuberLoss() #%% 定义优化算法 trainer torch.optim.SGD(net.parameters(), lr0.03) # optim module中的SGD #%% 训练 num_epochs 3 for epoch in range(num_epochs): for X, y in data_iter: l loss(net(X), y) trainer.zero_grad() l.backward() trainer.step() l loss(net(features), labels) print(fepoch {epoch1}, loss {l:f}) #%% 查看误差 w net[0].weight.data print(w的估计误差, true_w - w.reshape(true_w.shape)) b net[0].bias.data print(b的估计误差, true_b - b)输出结果3.如何访问线性回归的梯度#假如是多层网络可以用一个self.xxx某层然后在外面通过net.xxx.weight.grad和net.xxx.bias.grad把梯度给拿出来。 net[0].weight.grad net[0].bias.grad输出结果四、实验心得通过此次实验我熟悉了PyTorch框架以及PyTorch的基本概念、数据结构和核心函数创建了线性回归模型使用PyTorch构建一个简单的线性回归模型完成了线性回归从零开始实现及其简洁实现以及章节后习题。明白了以下几点1.深度学习的关键要素包括训练数据、损失函数、优化算法以及模型本身。这些要素相互作用共同决定了模型的性能和表现。2.矢量化是一种重要的数学表达方式它能使数学计算更加简洁高效。通过使用向量和矩阵运算可以将复杂的计算过程转化为简单的线性代数运算从而提高计算效率。3.最小化目标函数和执行极大似然估计是等价的。在机器学习中通常通过最小化损失函数来优化模型。而在概率统计中可以通过最大化似然函数来估计模型参数。这两种方法在数学上是等价的都可以用于优化模型。4.线性回归模型可以被看作是一个简单的神经网络模型。它只包含一个输入层和一个输出层其中输入层的神经元数量与输入特征的维度相同输出层的神经元数量为1。线性回归模型可以用于解决回归问题通过学习输入特征与输出之间的线性关系来进行预测。5.在深度学习中学习了如何实现和优化深度神经网络。在这个过程中只使用张量Tensor和自动微分Automatic Differentiation而不需要手动定义神经网络层或复杂的优化器。张量是深度学习中的基本数据结构它可以表示多维数组并支持各种数学运算。自动微分是一种计算梯度的技术它能够自动计算函数相对于输入的导数从而实现了反向传播算法。6.为了更加简洁地实现模型可以利用PyTorch的高级API。在PyTorch中data模块提供了数据处理工具包括数据加载、预处理和批处理等功能使得数据的处理变得更加方便和高效。nn模块则提供了大量的神经网络层和常见损失函数的定义可以直接使用这些层和函数来构建和训练模型无需手动实现。此外还可以通过使用_结尾的方法来进行参数的替换和初始化从而更加灵活地管理模型的参数。