<jupyter_start><jupyter_text>第一课 当深度学习遇上PyTorch在这节课中，我们主要展示了PyTorch的使用方法，以及如何用PyTorch实现一个线性回归算法本文件是集智AI学园http://campus.swarma.org 出品的“火炬上的深度学习”第II课的配套源代码  一、有关Tensor和Autograd变量的练习 1. Tensor a. 产生Tensor<jupyter_code>import torch  #导入torch包
torch.__version__ #显示当前PyTorch版本号，笔者用的是0.1.12_2，有些命令可能在新的版本下无法执行，请参考PyTorch文件找到最新的相应命令
x = torch.rand(5, 3)  #产生一个5*3的tensor，随机取值
x  #显示x的值
y = torch.ones(5, 3) #产生一个5*3的Tensor，元素都是1
y
z = torch.zeros(2, 5, 3) #产生一个5*3的Tensor，元素都是1
z
z[0]
x[1,2]
x[:,2]<jupyter_output><empty_output><jupyter_text>b. Tensor的运算<jupyter_code>#x = torch.FloatTensor([[0.3297,0.7021,0.1119],[0.6668,0.6904,0.1953],[0.6683,0.4260,0.2950],[0.0899,0.4099,0.0882],[0.4675,0.8369,0.1926]])
z = x + y #两个tensor可以直接相加
z<jupyter_output><empty_output><jupyter_text>下面的语句展示了两个tensor按照矩阵的方式相乘，注意x的尺寸是5*3，y的尺寸也是5*3无法进行矩阵乘法，所以先将y进行转置。转置操作可以用.t来完成，也可以用.transpose(0, 1)来完成<jupyter_code>q = x.mm(y.t()) #x乘以y的转置
q<jupyter_output><empty_output><jupyter_text>c. Tensor与numpy.ndarray之间的转换<jupyter_code>import numpy as np #导入numpy包
a = np.ones([5, 3]) #建立一个5*3全是1的二维数组（矩阵）
b = torch.from_numpy(a) #利用from_numpy将其转换为tensor
b
c = torch.FloatTensor(a) #另外一种转换为tensor的方法，类型为FloatTensor，还可以使LongTensor，整型数据类型
c
b.numpy()  #从一个tensor转化为numpy的多维数组<jupyter_output><empty_output><jupyter_text>tensor和numpy的最大区别在于tensor可以在GPU上运算<jupyter_code>if torch.cuda.is_available():  #检测本机器上有无GPU可用
    x = x.cuda() #返回x的GPU上运算的版本
    y = y.cuda()
    print(x + y) #tensor可以在GPU上正常运算
if torch.cuda.is_available():  #检测本机器上有无GPU可用
    device = torch.device("cuda")          # 选择一个CUDA设备
    y = torch.ones_like(x, device=device)  # 在GPU上直接创建张量
    x = x.to(device)                       # 也可以直接加载``.to("cuda")``
    z = x + y
    print(z)
    print(z.to("cpu", torch.double))       # 转回到CPU上``.to``<jupyter_output><empty_output><jupyter_text>2. 有关自动微分变量<jupyter_code>x = torch.ones((2, 2), requires_grad=True)  
x
y = x + 2  #可以按照Tensor的方式进行计算
y.grad_fn  
#注：在新版本PyTorch中，可以用.grad_fn
z = y * y  #可以进行各种符合运算
z.grad_fn
z = torch.mean(y * y)  #也可以进行复合运算
z.data #.data属性可以返回z所包裹的tensor<jupyter_output><empty_output><jupyter_text>** backward可以实施反向传播算法，并计算所有计算图上叶子节点的导数（梯度）信息。注意，由于z和y都不是叶子节点，所以都没有梯度信息）**<jupyter_code>z.backward() #梯度反向传播
print(z.grad)
print(y.grad)
print(x.grad)<jupyter_output><empty_output><jupyter_text>在下面的例子中，我们让矩阵x反复作用在向量x上，系统会自动记录中间的依赖关系和长路径<jupyter_code>s = torch.tensor([[0.01, 0.02]], requires_grad = True) #创建一个1*2的tensor（1维向量）
x = torch.ones(2, 2, requires_grad = True) #创建一个2*2的矩阵型tensor
for i in range(10):
    s = s.mm(x)  #反复用s乘以x（矩阵乘法），注意s始终是1*2的tensor
z = torch.mean(s) #对s中的各个元素求均值，得到一个1*1的scalar（标量，即1*1张量）
z.backward() #在具有很长的依赖路径的计算图上用反向传播算法计算叶节点的梯度
print(x.grad)  #x作为叶节点可以获得这部分梯度信息
print(s.grad)  #s不是叶节点，没有梯度信息<jupyter_output><empty_output><jupyter_text>二、利用PyTorch实现简单的线性回归算法  1. 准备数据在这里，我们人为生成一些样本点作为我们的原始数据本文件是集智AI学园http://campus.swarma.org 出品的“火炬上的深度学习”第II课的配套源代码<jupyter_code>x = torch.linspace(0, 100).type(torch.FloatTensor) #linspace可以生成0-100之间的均匀的100个数字
rand = torch.randn(100) * 10 #随机生成100个满足标准正态分布的随机数，均值为0，方差为1.将这个数字乘以10，标准方差变为10
y = x + rand #将x和rand相加，得到伪造的标签数据y。所以(x,y)应能近似地落在y=x这条直线上<jupyter_output><empty_output><jupyter_text>划分数据集<jupyter_code>x_train = x[: -10]
x_test = x[-10 :]
y_train = y[: -10]
y_test = y[-10 :]<jupyter_output><empty_output><jupyter_text>将生成的训练数据点画在图上<jupyter_code>import matplotlib.pyplot as plt #导入画图的程序包
plt.figure(figsize=(10,8)) #设定绘制窗口大小为10*8 inch
plt.plot(x_train.numpy(), y_train.numpy(), 'o') 
plt.xlabel('X') #添加X轴的标注
plt.ylabel('Y') #添加Y周的标注
plt.show() #将图形画在下面<jupyter_output><empty_output><jupyter_text>2. 构造模型，计算损失函数 在下面的代码中，需要注意expand_as和mul的使用。首先，a的维度为1，x的维度为100*1的Tensor，这两者不能直接相乘，因为维度不同。所以，先要将a升维成1*1的Tensor。这就好比将原本在直线上的点被升维到了二维平面上，同时直线仍然在二维平面中。```expand_as(x)```可以将张量升维成与x同维度的张量。所以如果a = 1, x为尺寸为100，那么，a.expand_as(x)$ = (1, 1, \cdot\cdot\cdot, 1)^T$```x * y```为两个1维张量的乘积，计算结果：$(x * y)_i = x_i \cdot y_i$<jupyter_code>a = torch.rand(1, requires_grad = True)
b = torch.rand(1, requires_grad = True)
predictions = a.expand_as(x_train) * x_train + b.expand_as(x_train)
predictions
loss = torch.mean((predictions - y_train) ** 2)  #计算损失函数
loss.backward() #开始反向传播梯度

#开始梯度下降，其中0.001为学习率
a.data.add_(- 0.001 * a.grad.data) 
b.data.add_(- 0.001 * b.grad.data)

#注意我们无法改变一个tensor，而只能对tensor的data属性做更改
#所有函数加“_”都意味着需要更新调用者的数值。<jupyter_output><empty_output><jupyter_text>3. 训练模型的代码  a. 错误版本错误在于，每一步迭代周期没有将a和b的梯度（grad）数值设置为0，导致每一步backward候梯度就会不断累加<jupyter_code>a = torch.rand(1, requires_grad = True)
b = torch.rand(1, requires_grad = True)
print('Initial parameters:', a, b)
learning_rate = 0.0001
for i in range(1000):
    predictions = a.expand_as(x_train) * x_train + b.expand_as(x_train)
    loss = torch.mean((predictions - y_train) ** 2)
    print('loss:', loss)
    loss.backward()
    a.data.add_(- learning_rate * a.grad.data)
    b.data.add_(- learning_rate * b.grad.data)<jupyter_output><empty_output><jupyter_text>通过打印输出的loss结果来看，存在着非常大的震荡，从而导致无法正确估计参数a和b的值  b. 正确版本<jupyter_code>a = torch.rand(1, requires_grad = True) #创建a变量，并随机赋值初始化
b = torch.rand(1, requires_grad = True) #创建b变量，并随机赋值初始化
print('Initial parameters:', [a, b])
learning_rate = 0.0001 #设置学习率
for i in range(1000):
    predictions = a.expand_as(x_train) * x_train + b.expand_as(x_train)  #计算在当前a、b条件下的模型预测数值
    loss = torch.mean((predictions - y_train) ** 2) #通过与标签数据y比较，计算误差
    print('loss:', loss)
    loss.backward() #对损失函数进行梯度反传
    a.data.add_(- learning_rate * a.grad.data)  #利用上一步计算中得到的a的梯度信息更新a中的data数值
    b.data.add_(- learning_rate * b.grad.data)  #利用上一步计算中得到的b的梯度信息更新b中的data数值
    ### 增加了这部分代码，清空存储在变量a，b中的梯度信息，以免在backward的过程中会反复不停地累加
    a.grad.data.zero_() #清空a的梯度数值
    b.grad.data.zero_() #清空b的梯度数值
x_data = x_train.data.numpy() # 获得x包裹的数据
plt.figure(figsize = (10, 7)) #设定绘图窗口大小
xplot, = plt.plot(x_data, y_train.numpy(), 'o') # 绘制原始数据
yplot, = plt.plot(x_data, a.data.numpy() * x_data + b.data.numpy())  #绘制拟合数据
plt.xlabel('X') #更改坐标轴标注
plt.ylabel('Y') #更改坐标轴标注
str1 = str(a.data.numpy()[0]) + 'x +' + str(b.data.numpy()[0]) #图例信息
plt.legend([xplot, yplot],['Data', str1]) #绘制图例
plt.show()<jupyter_output><empty_output><jupyter_text>3. 测试阶段<jupyter_code>predictions = a.expand_as(x_test) * x_test + b.expand_as(x_test) #计算模型的预测结果
predictions #输出
x_data = x_train.data.numpy() # 获得x包裹的数据
x_pred = x_test.data.numpy()
plt.figure(figsize = (10, 7)) #设定绘图窗口大小
plt.plot(x_data, y_train.data.numpy(), 'o') # 绘制训练数据
plt.plot(x_pred, y_test.data.numpy(), 's') # 绘制测试数据
x_data = np.r_[x_data, x_test.data.numpy()]
plt.plot(x_data, a.data.numpy() * x_data + b.data.numpy())  #绘制拟合数据
plt.plot(x_pred, a.data.numpy() * x_pred + b.data.numpy(), 'o') #绘制预测数据
plt.xlabel('X') #更改坐标轴标注
plt.ylabel('Y') #更改坐标轴标注
str1 = str(a.data.numpy()[0]) + 'x +' + str(b.data.numpy()[0]) #图例信息
plt.legend([xplot, yplot],['Data', str1]) #绘制图例
plt.show()<jupyter_output><empty_output><jupyter_text>本文件是集智AI学园http://campus.swarma.org 出品的“火炬上的深度学习”第II课的配套源代码<jupyter_code>torch.FloatTensor(np.zeros(10))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.16223595344158642, 'fraction_numerical': 0.035924701824974856, 'mean_word_length': 6.639956092206367, 'pattern_counts': {'":': 0, '<': 65, '<?xml version=': 0, '>': 65, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '16', 'dir': 'jupyter-structured-clean-dedup', 'id': '316897', 'license': 'Apache-2.0', 'n_tokens_mistral': 4147, 'n_tokens_neox': 4360, 'n_words': 398, 'path': '02_DL_meets_PyTorch/初识PyTorch basics.ipynb', 'repo_name': 'swarmapytorch/book_DeepLearning_in_PyTorch_Source', 'size': '6959'}