200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > 《neural networks and deep learning》读书笔记

《neural networks and deep learning》读书笔记

时间:2019-09-29 00:46:55

相关推荐

《neural networks and deep learning》读书笔记

neural networks and deep learning

项目地址:/mnielsen/neural-networks-and-deep-learning.git

1.使用neural nets识别手写字

感知机(perceptron),阶跃函数,$\epsilon(z)=\begin{cases} 1 & \text{z>0}\ 0 & \text{z<0} \end{cases} $:

n个二进制输入xi,一个二进制输出y, y ( x ) = ε ( w ⋅ x + b ) y(x)=\varepsilon(w\cdot x+b) y(x)=ε(w⋅x+b);可模拟基本逻辑函数。原理是:通过加权证据做出决策。偏置bias:度量感知机激活的难易程度。组成的网络在权重和偏置的微小变化可能导致输出的剧烈变化

主要使用的神经元模型为sigmoid neuron, σ ( z ) = 1 1 + e − z \sigma(z)=\frac{1}{1+e^{-z}} σ(z)=1+e−z1​:

学习原理:权重和偏置的微小变化可导致输出的微小变化

数据集MNIST,训练集6万(包括1万验证集),测试集1万;大小28*28、灰度图

二次损失函数,均方误差(MSE): C ( w , b ) = 1 2 n ∑ x ∣ ∣ y ( x ) − a ∣ ∣ 2 C(w,b)=\frac{1}{2n}\sum_x ||y(x)-a||^2 C(w,b)=2n1​∑x​∣∣y(x)−a∣∣2,衡量预测与真值的近似程度,应尽可能的小。1/2方便求导。

梯度下降(gradient descent):

Δ C = ∇ C ⋅ ∇ v = ( ∂ C ∂ w , ∂ C ∂ b ) T ⋅ ( ∇ w , ∇ b ) \Delta C=\nabla C \cdot \nabla v=(\frac{\partial C}{\partial w },\frac{\partial C}{\partial b })^T\cdot(\nabla w,\nabla b) ΔC=∇C⋅∇v=(∂w∂C​,∂b∂C​)T⋅(∇w,∇b)。令 ∇ v = − η ∇ C \nabla v=-\eta \nabla C ∇v=−η∇C;则 ∇ C \nabla C ∇C一直为负值,持续迭代则可使 C C C最小, η \eta η为学习率。所以分类问题变为最优化问题:

求梯度 ∇ C \nabla C ∇C。计算 v → v , = v − η ∇ C v\to v^,=v-\eta \nabla C v→v,=v−η∇C,使 w , b w,b w,b向梯度的反方向移动,使 C C C降低。重复1,2使 C C C最小。
注意: ∇ C \nabla C ∇C是在整个训练集上进行求解的,如果每次迭代都在整个集合上进行,则非常慢。

随机梯度下降(stochastic gradient descent):用随机抽取的一小样本集上的梯度 ∇ C x \nabla C_x ∇Cx​估计总体梯度 ∇ C \nabla C ∇C。即 ∇ C ≈ 1 m ∑ j = 1 m ∇ C x j \nabla C \approx \frac{1}{m} \sum_{j=1}^m \nabla C_{x_j} ∇C≈m1​∑j=1m​∇Cxj​​。小样本集即为mini-batch

将整个训练集分为n个互斥mini-batch,对每个mini-batch执行后,称为一个epoch

w、b迭代公式为: w k → w k , = w k − η m ∑ j ∂ C x j ∂ w k b l → b l , = b l − η m ∑ j ∂ C x j ∂ b l \begin{aligned}w_k \to w_k^,=w_k-\frac{\eta}{m}\sum_j \frac{\partial C_{x_j}}{\partial w_k} \\ b_l\to b_l^,=b_l-\frac{\eta}{m}\sum_j \frac{\partial C_{x_j}}{\partial b_l}\end{aligned} wk​→wk,​=wk​−mη​j∑​∂wk​∂Cxj​​​bl​→bl,​=bl​−mη​j∑​∂bl​∂Cxj​​​​

以上的 w , b w,b w,b代表网络中所有的参数,是矩阵。

整个算法流程

参数 w , b w,b w,b随机初始化;mini-batch里的每个样本 x i x_i xi​作为输入,通过前向传播(feedforward)计算输出值 y ( x i ) y(x_i) y(xi​);计算损失函数(cost function),根据链式法则、反向传播(backpropagation)计算所有参数的变化值。求mini-batch上的平均值,并更新所有 w , b w,b w,b参数。重复2-4,直到完成规定的epoch或者,损失函数小于一定值。

准确率95%左右,增加隐藏层到100,达96%左右。增大学习率,准确率明星降低。

2.backpropagation如何工作的

符号表示:

w j k l w_{jk}^l wjkl​:表示连接 l − 1 l-1 l−1层的第 k k k个神经元与 l l l层的第 j j j个神经元的权重。

b j l b_j^l bjl​:表示 l l l层的第 j j j个神经元的偏置。

a j l a_j^l ajl​:表示 l l l层的第 j j j个神经元的激励,这个神经元向下一层的输出。

三者间的关系:

KaTeX parse error: No such environment: equation* at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲*̲}̲a_j^l=\sigma(\s…

其中 w l ∈ R M × K w^l\in R^{M\times K} wl∈RM×K, b ∈ R M b\in R^M b∈RM;M为l层的神经元个数,K为l-1层的神经元个数。

The four fundamental equations behind backpropagation

定义l层第j个神经元的误差error: δ j l = ∂ C ∂ z j l \delta_j^l=\frac{\partial C}{\partial z_j^l} δjl​=∂zjl​∂C​,表示l层第j个神经元上发生 Δ z j l \Delta z_j^l Δzjl​的变化,导致C的变化程度,则:

KaTeX parse error: No such environment: gather* at position 8: \begin{̲g̲a̲t̲h̲e̲r̲*̲}̲ \delta_j^L = …

证明:链式求导法则: z = f ( u , v ) , u = φ ( x , y ) , v = ψ ( x , y ) z=f(u,v),u=\varphi(x,y),v=\psi(x,y) z=f(u,v),u=φ(x,y),v=ψ(x,y),则 ∂ z ∂ x = ∂ z ∂ u ∂ u ∂ x + ∂ z ∂ v ∂ v ∂ x \frac{\partial z}{\partial x}=\frac{\partial z}{\partial u}\frac{\partial u}{\partial x}+\frac{\partial z}{\partial v}\frac{\partial v}{\partial x} ∂x∂z​=∂u∂z​∂x∂u​+∂v∂z​∂x∂v​

δ j L = ∂ C ∂ z j L = ∑ k ∂ C ∂ a k L ∂ a k L ∂ z j L = 当 j ≠ k , 则 后 面 一 项 为 0 ∂ C ∂ a j L σ ′ ( z j L ) \delta_j^L=\frac{\partial C}{\partial z_j^L}=\sum_k \frac{\partial C}{\partial a_k^L}\frac{\partial a_k^L}{\partial z_j^L}\xlongequal{当j\neq k,则后面一项为0}\frac{\partial C}{\partial a_j^L}\sigma'(z_j^L) δjL​=∂zjL​∂C​=∑k​∂akL​∂C​∂zjL​∂akL​​当j​=k,则后面一项为0 ∂ajL​∂C​σ′(zjL​)。

因为: z k l + 1 = ∑ j w k j l + 1 σ ( z j l ) + b k l + 1 ⇒ ∂ z k l + 1 ∂ z j l = w k j l + 1 σ ′ ( z j l ) z_k^{l+1}=\sum_j w_{kj}^{l+1}\sigma(z_j^l)+b_k^{l+1}\Rightarrow\frac{\partial z_k^{l+1}}{\partial z_j^l}=w_{kj}^{l+1}\sigma'(z_j^l) zkl+1​=∑j​wkjl+1​σ(zjl​)+bkl+1​⇒∂zjl​∂zkl+1​​=wkjl+1​σ′(zjl​),k为下标;

所以: δ j l = ∂ C ∂ z j l = ∑ k ∂ C ∂ z k l + 1 ∂ z k l + 1 ∂ z j l = ∑ k δ k l + 1 ∂ z k l + 1 ∂ z j l = ∑ k w k j l + 1 δ k l + 1 σ ′ ( z j l ) \delta_j^l=\frac{\partial C}{\partial z_j^l}=\sum_k\frac{\partial C}{\partial z_k^{l+1}}\frac{\partial z_k^{l+1}}{\partial z_j^l}=\sum_k \delta_k^{l+1}\frac{\partial z_k^{l+1}}{\partial z_j^l}=\sum_k w_{kj}^{l+1}\delta_k^{l+1}\sigma'(z_j^l) δjl​=∂zjl​∂C​=∑k​∂zkl+1​∂C​∂zjl​∂zkl+1​​=∑k​δkl+1​∂zjl​∂zkl+1​​=∑k​wkjl+1​δkl+1​σ′(zjl​)。k为l+1层神经元的个数;

因为: z j l = ∑ k w j k l a k l − 1 + b j l ⇒ ∂ z j l ∂ b j l = 1 ; ∂ z j l ∂ w j k l = a k l − 1 z_j^l=\sum_k w_{jk}^l a_k^{l-1}+b_j^l \Rightarrow \frac{\partial z_j^l}{\partial b_j^l}=1;\frac{\partial z_j^l}{\partial w_{jk}^l}=a_k^{l-1} zjl​=∑k​wjkl​akl−1​+bjl​⇒∂bjl​∂zjl​​=1;∂wjkl​∂zjl​​=akl−1​;所以:

∂ C ∂ b j l = ∑ k ∂ C ∂ z k l ∂ z k l ∂ b j l = 当 j ≠ k , 则 后 面 一 项 为 0 δ j l \frac{\partial C}{\partial b_j^l}=\sum_k\frac{\partial C}{\partial z_k^l}\frac{\partial z_k^l}{\partial b_j^l}\xlongequal{当j\neq k,则后面一项为0}\delta_j^l ∂bjl​∂C​=∑k​∂zkl​∂C​∂bjl​∂zkl​​当j​=k,则后面一项为0 δjl​; ∂ C ∂ w j k l = ∑ i ∂ C ∂ z i l ∂ z i l ∂ w j k l = 当 i ≠ j , 则 后 面 一 项 为 0 δ j l a k l − 1 \frac{\partial C}{\partial w_{jk}^l}=\sum_i\frac{\partial C}{\partial z_i^l}\frac{\partial z_i^l}{\partial w_{jk}^l}\xlongequal{当i\neq j,则后面一项为0}\delta_j^la_k^{l-1} ∂wjkl​∂C​=∑i​∂zil​∂C​∂wjkl​∂zil​​当i​=j,则后面一项为0 δjl​akl−1​。

代码:

class Network(object):def __init__(self , sizes):self.num_layers = len(sizes)self.sizes = sizesself.biases = [np.random.randn(y, 1) for y in sizes[1:]] #第一是输入层,没有偏置self.weights = [np.random.randn(y, x) for x, y in zip(sizes[:-1], sizes[1:])]def feedforward(self, a):for b, w in zip(self.biases, self.weights):a = sigmoid(np.dot(w, a)+b)return adef SGD(self, training_data, epochs, mini_batch_size, eta, test_data=None):if test_data: n_test = len(test_data)n = len(training_data)for j in xrange(epochs):random.shuffle(training_data)mini_batches = [training_data[k:k+mini_batch_size] for k in xrange(0, n, mini_batch_size)]for mini_batch in mini_batches:self.update_mini_batch(mini_batch, eta) #更新参数if test_data:print "Epoch {0}: {1} / {2}".format(j, self.evaluate(test_data), n_test)else:print "Epoch {0} complete".format(j)def update_mini_batch(self, mini_batch, eta):nabla_b = [np.zeros(b.shape) for b in self.biases]nabla_w = [np.zeros(w.shape) for w in self.weights]for x, y in mini_batch:delta_nabla_b, delta_nabla_w = self.backprop(x, y) #求每个样本参数的梯度,再求和nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]self.weights = [w-(eta/len(mini_batch))*nw for w, nw in zip(self.weights, nabla_w)] #更新参数self.biases = [b-(eta/len(mini_batch))*nb for b, nb in zip(self.biases, nabla_b)]def backprop(self, x, y):nabla_b = [np.zeros(b.shape) for b in self.biases]nabla_w = [np.zeros(w.shape) for w in self.weights]# feedforwardactivation = xactivations = [x] zs = [] for b, w in zip(self.biases, self.weights):z = np.dot(w, activation)+b #每个神经元的输入zs.append(z)activation = sigmoid(z) #每个神经元的输出activations.append(activation)# backward passdelta = self.cost_derivative(activations[-1], y) * sigmoid_prime(zs[-1]) #最后一层的deltanabla_b[-1] = deltanabla_w[-1] = np.dot(delta, activations[-2].transpose())for l in xrange(2, self.num_layers): #依次往前计算delta_w,delta_bz = zs[-l]sp = sigmoid_prime(z)delta = np.dot(self.weights[-l+1].transpose(), delta) * sp #计算前面一层的delta^(l-1)nabla_b[-l] = deltanabla_w[-l] = np.dot(delta, activations[-l-1].transpose())return (nabla_b, nabla_w)def evaluate(self, test_data):test_results = [(np.argmax(self.feedforward(x)), y) for (x, y) in test_data]return sum(int(x == y) for (x, y) in test_results)def cost_derivative(self, output_activations, y):return (output_activations-y)def sigmoid(z):return 1.0/(1.0+np.exp(-z))def sigmoid_prime(z):return sigmoid(z)*(1-sigmoid(z))

3. Improving the way neural networks learn

使用sigmoid函数,当输出接近1,变化缓慢;如图右上角最简单的模型,输入1,输出0;对于二次损失函数 C = ( y − a ) 2 2 C=\frac{(y-a)^2}{2} C=2(y−a)2​,可以求得导数 ∂ C ∂ w = ( a − y ) σ ′ ( z ) x = a σ ′ ( z ) \frac{\partial C}{\partial w}=(a-y)\sigma'(z)x=a\sigma'(z) ∂w∂C​=(a−y)σ′(z)x=aσ′(z), ∂ C ∂ b = ( a − y ) σ ′ ( z ) = a σ ′ ( z ) \frac{\partial C}{\partial b}=(a-y)\sigma'(z)=a\sigma'(z) ∂b∂C​=(a−y)σ′(z)=aσ′(z)。当w,b初始值为2时,学习会如图一样非常慢。

cross-entropy cost function

C = − 1 n ∑ x [ y ln ⁡ a + ( 1 − y ) ln ⁡ ( 1 − a ) ] (eq1) C=-\frac{1}{n}\sum_x[y\ln a+(1-y)\ln(1-a)]\tag{eq1} C=−n1​x∑​[ylna+(1−y)ln(1−a)](eq1)

偏导为:

∂ C ∂ w j = 1 n ∑ x σ ′ ( z ) x j σ ( z ) ( 1 − σ ( z ) ) ( σ ( z ) − y ) (eq2) \frac{\partial C}{\partial w_j}=\frac{1}{n}\sum_x \frac{\sigma'(z)x_j}{\sigma(z)(1-\sigma(z))}(\sigma(z)-y) \tag{eq2} ∂wj​∂C​=n1​x∑​σ(z)(1−σ(z))σ′(z)xj​​(σ(z)−y)(eq2)

sigmoid函数的导数为: σ ′ ( z ) = σ ( z ) ( 1 − σ ( z ) ) \sigma'(z)=\sigma(z)(1-\sigma(z)) σ′(z)=σ(z)(1−σ(z));则eq2变为:

∂ C ∂ w j = 1 n ∑ x x j ( σ ( z ) − y ) (eq3) \frac{\partial C}{\partial w_j}=\frac{1}{n}\sum_x x_j(\sigma(z)-y) \tag{eq3} ∂wj​∂C​=n1​x∑​xj​(σ(z)−y)(eq3)

参数学习的速度正比于输出的偏差,越大变化越快。准确率相比之前提高一点点。

Softmax:定义一个新的输出层,在输出层不使用sigmoid函数,而是通过以下公式产生输出的概率发布。

a j L = e z j L ∑ k e z k L (eq4) a_j^L=\frac{e^{z_j^L}}{\sum_k e^{z_k^L}} \tag{eq4} ajL​=∑k​ezkL​ezjL​​(eq4)

损失函数: C = − ln ⁡ a j L C=-\ln a_j^L C=−lnajL​。

Overfitting and regularization:测试集上的效果并没有像训练集那样好。

early stopping(hold out method):使用验证集来确定训练过程中,在验证集上出现效果最好的时候作为最终模型,

降低过拟合最简单的办法是增加训练集数量。或者降低网络的规模。

或者正则化(regularization):如L2正则化,

C = C 0 + λ 2 n ∑ w w 2 (eq5) C=C_0+\frac{\lambda}{2n}\sum_w w^2 \tag{eq5} C=C0​+2nλ​w∑​w2(eq5)

Dropout:通过修改网络结构,通过每次mini-batch随机删掉部分神经元,训练多个网络,然后用如投票的方式组合成更好的网络。

Artificially expanding the training data:微调数据,增加训练集数量。

Weight initialization

rectified linear unit: max ⁡ ( 0 , z ) \max(0,z) max(0,z),

代码:

class QuadraticCost(object):@staticmethoddef fn(a, y):return 0.5*np.linalg.norm(a-y)**2@staticmethoddef delta(z, a, y):return (a-y) * sigmoid_prime(z)class CrossEntropyCost(object):@staticmethoddef fn(a, y):return np.sum(np.nan_to_num(-y*np.log(a)-(1-y)*np.log(1-a)))@staticmethoddef delta(z, a, y):return (a-y)class Network(object):……def default_weight_initializer(self):self.biases = [np.random.randn(y, 1) for y in self.sizes[1:]]# 更改初始化方法,效果好一点self.weights = [np.random.randn(y,x)/np.sqrt(x) for x, y in zip(self.sizes[:-1], self.sizes[1:])]def update_mini_batch(self, mini_batch, eta, lmbda, n):……self.weights = [(1-eta*(lmbda/n))*w-(eta/len(mini_batch))*nw #添加了正则项for w, nw in zip(self.weights, nabla_w)]self.biases = [b-(eta/len(mini_batch))*nbfor b, nb in zip(self.biases, nabla_b)]def backprop(self, x, y):……delta = (self.cost).delta(zs[-1], activations[-1], y)nabla_b[-1] = deltanabla_w[-1] = np.dot(delta, activations[-2].transpose())……

4. A visual proof that neural nets can compute any function

5.Why are deep neural networks hard to train?

对于一些函数来说,浅层网络相比深层网络,需要的神经元呈指数级的增长。unstable gradientsvanishing gradient problem前面的隐藏层比后面的隐藏层学得慢。如图所示,黄色的条表示参数的变化幅度。exploding gradient problem:与vanishing相反,

6.Deep learning

卷积神经网络的基本概念:

Local receptive fields:隐藏层的一个神经元只连接原图的一小部分相邻区域Shared weights and biases:将局部感受域在整个图上滑动,作为隐藏层不同神经元的输入,连接中的权重和偏置不改变。并将该输入层到隐藏层的映射称为一个feature map。一组共享的权重和偏置确定了一个feature map。这组参数也称为kernel or filterPooling layers:简化卷积层输出的信息,如:L2 poolingMax-pooling

实践:

之前的网络,测试集上的准确率为97.80%。

卷积神经网络:准确率为98.78%。

继续添加卷积池化层,准确率可达99.06%;第二个卷积层的输入为20个12*12的feature map,设置了40个kernel,每个kernel的参数为20*5*5+1,对应将20个feature map上的同一感受野域作为输入。

将sigmoid函数改为rectified linear units(ReLU),准确率为99.23%。

扩展训练集,准确率为99.37%;再添加一个全连接层,准确率为99.43%;再利用dropout获得99.60%。

深度学习框架PyTorch:入门与实践

项目地址:/chenyuntc/pytorch-book

2 快速入门

2.2 PyTorch 入门第一步

Tensor是PyTorch中重要的数据结构,可以是一个数(标量)、一维数组(向量)、二维数组(矩阵)以及更高维的数组。

autograd:自动微分,autograd.Variable是Autograd中的核心类,它简单封装了Tensor,并支持几乎所有Tensor有的操作。Tensor在被封装为Variable之后,可以调用它的.backward实现反向传播,自动计算所有梯度,Variable主要包含三个属性。

data:保存Variable所包含的Tensorgrad:保存data对应的梯度,grad也是个Variable,而不是Tensor,它和data的形状一样。 在反向传播过程中是累加的,这意味着每一次运行反向传播,梯度都会累加之前的梯度,所以反向传播之前需把梯度清零。grad_fn:指向一个Function对象,这个Function用来反向传播计算输入的梯度。

小试牛刀:CIFAR-10分类

import torch as timport torchvision as tvimport torchvision.transforms as transformsfrom torchvision.transforms import ToPILImageimport torch.nn as nnimport torch.nn.functional as Ffrom torch import optim#模型class Net(nn.Module):# 把网络中具有可学习参数的层放在构造函数__init__中。如果某一层(如ReLU)不具有可学习的参数,# 则既可以放在构造函数中,也可以不放,但建议不放在其中,而在forward中使用nn.functional代替。def __init__(self):super(Net, self).__init__()self.conv1 = nn.Conv2d(3, 6, 5) self.conv2 = nn.Conv2d(6, 16, 5) self.fc1 = nn.Linear(16*5*5, 120) self.fc2 = nn.Linear(120, 84)self.fc3 = nn.Linear(84, 10)# 只要在nn.Module的子类中定义了forward函数,backward函数就会自动被实现(利用autograd)。# 在forward 函数中可使用任何tensor支持的函数,还可以使用if、for循环、print、log等Python语法,# 写法和标准的Python写法一致。def forward(self, x): x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2)) x = F.max_pool2d(F.relu(self.conv2(x)), 2) x = x.view(x.size()[0], -1) x = F.relu(self.fc1(x))x = F.relu(self.fc2(x))x = self.fc3(x) return x# 网络的可学习参数通过net.parameters()返回,net.named_parameters可同时返回可学习的参数及名称。# 训练 def train(trainloader,net):# 交叉熵损失函数criterion = nn.CrossEntropyLoss() # 在反向传播计算完所有参数的梯度后,还需要使用优化方法来更新网络的权重和参数,optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)t.set_num_threads(8) #设置线程for epoch in range(2):running_loss = 0.0for i, data in enumerate(trainloader, 0): #0表示从0开始inputs, labels = data # 输入数据optimizer.zero_grad() # 梯度清零outputs = net(inputs) loss = criterion(outputs, labels) # 计算损失loss.backward() #反向传播optimizer.step() # 更新参数 running_loss += loss.item() # loss 是一个scalar,需要使用loss.item()来获取数值,不能使用loss[0]if i % 2000 == 1999: # 每2000个batch打印一下训练状态print('[%d, %5d] loss: %.3f' % (epoch+1, i+1, running_loss / 2000))running_loss = 0.0print('Finished Training')# 测试 def test(testloader,net):correct,total = 0,0# 由于测试的时候不需要求导,可以暂时关闭autograd,提高速度,节约内存with t.no_grad():for data in testloader:images, labels = dataoutputs = net(images)_, predicted = t.max(outputs, 1)total += labels.size(0)correct += (predicted == labels).sum()print('10000张测试集中的准确率为: %d %%' % (100 * correct / total)) def main():#数据加载transform = pose([ # 定义对数据的预处理transforms.ToTensor(), # 转为Tensortransforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),]) # 归一化# 训练集trainset = tv.datasets.CIFAR10(root='./',train=True, download=True,transform=transform)trainloader = t.utils.data.DataLoader(trainset, batch_size=4,shuffle=True, num_workers=2)# 测试集testset = tv.datasets.CIFAR10('./',train=False, download=True, transform=transform)testloader = t.utils.data.DataLoader(testset,batch_size=4, shuffle=False, num_workers=2)net=Net() train(trainloader,net)test(testloader,net)

3 Tensor and Autograd

3.1 tensor

从接口的角度来讲,对tensor的操作可分为两类:

torch.function,如torch.save等。tensor.function,如tensor.view等

函数名以_结尾的都是inplace方式, 即会修改调用者自己的数据,在实际应用中需加以区分。

Numpy和Tensor共享内存,当numpy的数据类型和Tensor的类型不一样的时候,数据会被复制,不会共享内存。

小试牛刀:线性回归

import torch as tfrom matplotlib import pyplot as pltdevice = t.device('cpu') #如果你想用gpu,改成t.device('cuda:0')def get_fake_data(batch_size=8):''' 产生随机数据:y=x*2+3,加上了一些噪声'''global devicex = t.rand(batch_size, 1, device=device) * 5y = x * 2 + 3 + t.randn(batch_size, 1, device=device)return x, ydef main():global devicet.manual_seed(1000) # 设置随机数种子,保证在不同电脑上运行时下面的输出一致w = t.rand(1, 1).to(device) # 随机初始化参数b = t.zeros(1, 1).to(device)lr =0.02 # 学习率for ii in range(50):x, y = get_fake_data(batch_size=4)# forward:计算lossy_pred = x.mm(w) + b.expand_as(y) # x@W等价于x.mm(w);for python3 onlyloss = 0.5 * (y_pred - y) ** 2 # 均方误差loss = loss.mean()# backward:手动计算梯度dloss = 1dy_pred = dloss * (y_pred - y)dw = x.t().mm(dy_pred)db = dy_pred.sum()# 更新参数w.sub_(lr * dw)b.sub_(lr * db)# 可视化plt.ion() # 打开交互模式if ii%10 ==0:plt.cla() #清除原有图像x = t.arange(0, 6).view(-1, 1)y = x.float().mm(w) + b.expand_as(x)x2, y2 = get_fake_data(batch_size=32) # 画图plt.plot(x.cpu().numpy(), y.cpu().numpy()) # 预测线plt.scatter(x2.numpy(), y2.numpy()) # 真实散点图,与训练的不是一批数据plt.xlim(0, 5)plt.ylim(0, 13)plt.pause(0.5)print('w: ', w.item(), 'b: ', b.item())

3.2 autograd

torch.autograd就是为方便用户使用,而专门开发的一套自动求导引擎,它能够根据输入和前向传播过程自动构建计算图,并执行反向传播。计算图(Computation Graph)是现代深度学习框架如PyTorch和TensorFlow等的核心,其为高效自动求导算法——反向传播(Back Propogation)提供了理论支持。

可以认为需要求导(requires_grad)的tensor即Variable。autograd记录对tensor的操作记录用来构建计算图。Variable提供了大部分tensor支持的函数,但其不支持部分inplace函数,因这些函数会修改tensor自身,而在反向传播中,variable需要缓存原来的tensor来计算反向传播梯度。如果想要计算各个Variable的梯度,只需调用根节点variable的backward方法,autograd会自动沿着计算图反向传播,计算每一个叶子节点的梯度。variable.backward(gradient=None, retain_graph=None, create_graph=None)

如果想要修改tensor的数值,但是又不希望被autograd记录,那么我么可以对tensor.data进行操作

扩展autograd:写一个Function,实现它的前向传播和反向传播代码,Function对应于计算图中的矩形, 它接收参数,计算并返回结果。

小试牛刀: 用Variable实现线性回归

import torch as tfrom matplotlib import pyplot as pltimport numpy as np…… def main():……# 随机初始化参数w = t.rand(1,1, requires_grad=True)b = t.zeros(1,1, requires_grad=True)losses = np.zeros(500)lr =0.02 # 学习率for ii in range(500):……losses[ii] = loss.item()# backward:手动计算梯度loss.backward()# 更新参数w.data.sub_(lr * w.grad.data) # w.datab.data.sub_(lr * b.grad.data)# 梯度清零w.grad.data.zero_()b.grad.data.zero_()……plt.cla() plt.ioff() # 关闭交互模式plt.plot(losses)plt.show()

4 神经网络工具箱nn

torch.nn的核心数据结构是Module,它是一个抽象概念,既可以表示神经网络中的某个层(layer),也可以表示一个包含很多层的神经网络。在实际使用中,最常见的做法是继承nn.Module,撰写自己的网络/层。

自定义层必须继承nn.Module,并且在其构造函数中需调用nn.Module的构造函数,super(Linear, self).__init__()在构造函数__init__中必须自己定义可学习的参数,并封装成Parameter之类的,forward函数实现前向传播过程。

PyTorch实现了神经网络中绝大多数的layer,这些layer都继承于nn.Module,封装了可学习参数parameter,并实现了forward函数,且很多都专门针对GPU运算进行了CuDNN优化,其速度和性能都十分优异。注意:输入的不是单个数据,而是一个batch。输入只有一个数据,则必须调用tensor.unsqueeze(0) 或 tensor[None]将数据伪装成batch_size=1的batch

将每一层的输出直接作为下一层的输入,这种网络称为前馈传播网络(feedforward neural network)。对于此类网络如果每次都写复杂的forward函数会有些麻烦,在此就有两种简化方式,ModuleList和Sequential。其中Sequential是一个特殊的module,它包含几个子Module,前向传播时会将输入一层接一层的传递下去。ModuleList也是一个特殊的module,可以包含几个子module,可以像用list一样使用它,但不能直接把输入传给ModuleList,当在Module中使用它的时候,就能自动识别为子module。

小试牛刀:搭建ResNet

from torch import nnimport torch as tfrom torch.nn import functional as Fclass ResidualBlock(nn.Module):'''实现子module: Residual Block'''def __init__(self, inchannel, outchannel, stride=1, shortcut=None):super(ResidualBlock, self).__init__()self.left = nn.Sequential(nn.Conv2d(inchannel,outchannel,3,stride, 1,bias=False),nn.BatchNorm2d(outchannel),nn.ReLU(inplace=True),nn.Conv2d(outchannel,outchannel,3,1,1,bias=False),nn.BatchNorm2d(outchannel) )self.right = shortcutdef forward(self, x):out = self.left(x)residual = x if self.right is None else self.right(x)out += residualreturn F.relu(out)class ResNet(nn.Module):'''实现主module:ResNet34ResNet34 包含多个layer,每个layer又包含多个residual block用子module来实现residual block,用_make_layer函数来实现layer'''def __init__(self, num_classes=1000):super(ResNet, self).__init__()# 前几层图像转换self.pre = nn.Sequential(nn.Conv2d(3, 64, 7, 2, 3, bias=False),nn.BatchNorm2d(64),nn.ReLU(inplace=True),nn.MaxPool2d(3, 2, 1))# 重复的layer,分别有3,4,6,3个residual blockself.layer1 = self._make_layer( 64, 64, 3)self.layer2 = self._make_layer( 64, 128, 4, stride=2)self.layer3 = self._make_layer( 128, 256, 6, stride=2)self.layer4 = self._make_layer( 256, 512, 3, stride=2)#分类用的全连接self.fc = nn.Linear(512, num_classes)def _make_layer(self, inchannel, outchannel, block_num, stride=1):'''构建layer,包含多个residual block'''shortcut = nn.Sequential(nn.Conv2d(inchannel,outchannel,1,stride, bias=False),nn.BatchNorm2d(outchannel))layers = []layers.append(ResidualBlock(inchannel, outchannel, stride, shortcut))for i in range(1, block_num):layers.append(ResidualBlock(outchannel, outchannel))return nn.Sequential(*layers)def forward(self, x):x = self.pre(x)x = self.layer1(x)x = self.layer2(x)x = self.layer3(x)x = self.layer4(x)x = F.avg_pool2d(x, 7)x = x.view(x.size(0), -1)return self.fc(x)

5. PyTorch常用工具模块

from torch.utils import datafrom torchvision import transforms as T

加载数据:自定义的数据集对象需要继承Dataset。实现两个方法:__getitem__:返回一条数据,或一个样本。obj[index]等价于obj.__getitem__(index)__len__:返回样本的数量。len(obj)等价于obj.__len__()。如果所有的文件按文件夹保存,每个文件夹下存储同一个类别的图片,文件夹名为类名,可用ImageFolder(root, transform=None, target_transform=None, loader=default_loader);label是按照文件夹名顺序排序后存成字典,即{类名:类序号(从0开始)}。 批处理:DataLoader(dataset, batch_size=1, shuffle=False, sampler=None, num_workers=0, collate_fn=default_collate, pin_memory=False, drop_last=False)数据处理:transform = pose()计算机视觉工具包torchvision:models、datasets、transforms转GPU,.cuda()

6.文件组织结构

- `checkpoints/`: 用于保存训练好的模型,可使程序在异常退出后仍能重新载入模型,恢复训练- `data/`:数据相关操作,包括数据预处理、dataset实现等- `models/`:模型定义,可以有多个模型,例如上面的AlexNet和ResNet34,一个模型对应一个文件- `utils/`:可能用到的工具函数,在本次实验中主要是封装了可视化工具- `config.py`:配置文件,所有可配置的变量都集中在此,并提供默认值- `main.py`:主文件,训练和测试程序的入口,可通过不同的命令来指定不同的操作和参数- `requirements.txt`:程序依赖的第三方库- `README.md`:提供程序的必要说明

批处理:DataLoader(dataset, batch_size=1, shuffle=False, sampler=None, num_workers=0, collate_fn=default_collate, pin_memory=False, drop_last=False)数据处理:transform = pose()计算机视觉工具包torchvision:models、datasets、transforms转GPU,.cuda()

6.文件组织结构

- `checkpoints/`: 用于保存训练好的模型,可使程序在异常退出后仍能重新载入模型,恢复训练- `data/`:数据相关操作,包括数据预处理、dataset实现等- `models/`:模型定义,可以有多个模型,例如上面的AlexNet和ResNet34,一个模型对应一个文件- `utils/`:可能用到的工具函数,在本次实验中主要是封装了可视化工具- `config.py`:配置文件,所有可配置的变量都集中在此,并提供默认值- `main.py`:主文件,训练和测试程序的入口,可通过不同的命令来指定不同的操作和参数- `requirements.txt`:程序依赖的第三方库- `README.md`:提供程序的必要说明

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。