基于Pytorch深度学习
在本文中,我将介绍一下PyTorch,PyTorch
是一个训练神经网络的有利助手。同时,Pytorch基本可以无缝衔接Numpy,对于熟悉Numpy的你,应该很容易就能转型到PyTorch。同时PyTorch能够调用GPU的API实现硬件加速,并提供诸如自动计算梯度和模型定制化等诸多方便功能。另一方面,PyTorch相较于
Tensorflow 有更好的兼容性。
神经网络
深度学习是一套基于人工神经网络的函数拟合方法。网络有多个“神经元”构成。每个神经元又有多个输入,每个输入又有自己的权重。这些输入将会权重加成后,输入激活函数得到输出值。
数学表达式为:
$$
\begin{align}
y &= f(w_1 x_1 + w_2 x_2 + b) \
y &=
f\left(\sum_i w_i x_i +b \right)
\end{align}
$$
这里的向量乘法为点乘。
$$
h =
\begin{bmatrix}
x_1 , x_2 \cdots x_n
\end{bmatrix}
\cdot
\begin{bmatrix}
w_1 \
w_2 \
\vdots \
w_n
\end{bmatrix}
$$
张量
通过对张量的线性运算,我们就能够得出各种各样的神经网络。张量其实是一种,矩阵的扩展表达形式,一维张量是标量,二维张量是向量,三维张量是矩阵(如下图所示)。
下面我们看一下如何使用PyTorch来构建一个简单的神经网络。
1 | # 首先引入 PyTorch |
1 | def activation(x): |
Sigmoid 函数:
1 | ### 生成数据 |
以上我们就完成了,训练简单神经网络的准备数据。现在他们还都是基于正态随机分布的随机取值,但是随着训练过程的进行,他们将收敛于GT。
Pytorch
的张量可以进行,相加,相乘,相减等操作,和你平常使用的Numpy的array用法一样。现在,我们将用生成的随机数据计算这个简单神经网络的输出值。
练习: 通过特征 features
,权重weights
,和偏移量bias
计算网络的输出值。类似与Numpy,在Pytorch中可以使用torch.sum()
函数进行求和,然后使用我们定义的激活函数来计算输出值。
1 | ### 解 |
你也可以用采用矩阵相乘的办法来一次完成相乘和求和的操作。通常来说,我更建议采用矩阵相乘的方式来进行计算,因为这样做更高效。PyTorch提供了大量的库函数和GPU接口,来加速矩阵运算。
如果我们想使用矩阵乘法,我们就需要调用函数torch.mm()
或者torch.matmul()
。我个人更建议使用后者,因为它有更多的特性。
1 | torch.matmul(features, weights) |
但是当我们运行时,就会出现如下错误
1 | >> torch.matmul(features, weights) |
这是因为张量的大小(shape)不正确,造成的矩阵不能相乘。这是一个非常常见的问题。
解决办法也很简单,就是直接调整weights
的大小,来适应矩阵乘法运算。
注意:
张量的大小标示为tensor.shape
。这是一个非常常见的运算函数,请记住它。
Pytorch 也提供了诸多适合改变shape(大小)的函数,例如:weights.reshape()
,weights.resize_()
,
和weights.view()
.
weights.reshape(a, b)
是讲weights
的数据**拷贝**到一个新的内存中,并整形为a*b。
*weights.resize_(a, b)
也能得到相同的结果,唯一不同的是,他会检查shape。当新tensor比原tensor的元素少时,多余的元素将会在新tensor中剔除(但你依然可以通过原tensor获得这部分数据),如果新tesnor的元素多于原tensor,那么程序将阻止它初始化。同时要注意,不同于reshape,这里的操作都是原地操作,没有拷贝。如果你想对原地操作有更过的了解可以查看
[read more about in-place operations](https://discuss.pytorch.org/t/what-is-in-
place-operation/16244) in PyTorch.
weights.view(a, b)
其实和reshape差不多,但是由于存在时间比较长,所以用的人也比较多。
我个人建议倾向于使用reshape
,但是如果你使用另外两个一般也不会影响你的使用结果。
练习: 使用矩阵相乘来计算神经元输出。
1 | ## 解 |
实现第一个网络吧!
现在我们已经学会了如何计算一个神经元。现在我们来试着把这些神经元堆叠在一起,从而试想一个网络,第一层的神经元的输出可以作为第二层神经元的输入。因为每层都有多个神经元,所以我们用矩阵来表示权重。
最底下的一层是神经网络的输入,我们称之为输入层。中间的称为隐藏层,最顶端的称为输出层。下面我们从数学的角度来分析一下网络的运算原理。例如,隐藏层($h_1$
和$h_2$)可表示为:
$$
\vec{h} = [h_1 , h_2] =
\begin{bmatrix}
x_1 , x_2 \cdots ,
x_n
\end{bmatrix}
\cdot
\begin{bmatrix}
w_{11} & w_{12} \
w_{21} &w_{22} \
\vdots &\vdots \
w_{n1} &w_{n2}
\end{bmatrix}
$$
隐藏层的输出就是输出层的输入。那么整个网络的输出就可以表达为:
$$
y = f_2 ! \left(, f_1 !
\left(\vec{x} , \mathbf{W_1}\right) \mathbf{W_2} \right)
$$
1 | ### 生成数据 |
练习: 使用权重
W1
,W2
和偏移量B1
,B2
计算神经网络的输出。
1 | ### 解 |
如果计算正确,你的输出为 tensor([[ 0.3171]])
.
隐藏单元的数目称为hyperparameter(超参数)。每个权重和偏移量的超参数都不尽相同。同时,有更多层,更多单元的网络在相同数据中有更好的性能,因为他们学习到更多的特征,当然计算量也越大。
Numpy 与 Torch 相互转换
和Numpy的相互转化是PyTorch的主打特性之一。具体操作为:
Numpy -> PyTorchtorch.from_numpy()
PyTorch -> Numpy
.numpy()
1 | import numpy as np |
1 | b = torch.from_numpy(a) # 转换成torch的张量 |
1 | b.numpy() # 转换回 numpy 的array |
注意这里所有的操作都是‘in-place’的。因为numpy和pyTorch共享内存。
1 | # pytorh乘2 |
1 | # Numpy array 也会有相应的变化,希望使用时大家能注意 |