深度学习PyTorch(一)pytorch基础&神经网络


科学计算库 numpy,数据分析库 pandas,可视化绘图工具Matplotlib

国内:百度的 apollo,duos 技术底层就是图像识别和语音识别技术,
讯飞的的语音识别技术用的是深度学习
人工智能四小龙:旷视 图森 商汤 依图
这几个企业里面面部识别,语音识别也用的深度学习

机器学习:图片–feature extracting-encoding-classification-分类结果
深度学习:所有的函数都从数据中自动学习
应用:语音识别 图像识别 人脸识别 机器翻译 自动驾驶
人脸识别 黑白图片上色 机器翻译 alphago下围棋 目标检测 自动驾驶
符号为编程:tensorflow
命令为编程:pytorch

目前神经网络框架分为静态图和动态图两种

PyTorch基础

Tensor

pytorch 与numpy之间的转换

import numpy as np

numpy_tensor=np.random.randn(10,20) 10*20的矩阵
pytorch_tensor=torch.from_numpy(numpy_tensor) 从numpy转化成torch
或者 pytorch_tensor=torch.Tensor(numpy_tensor) 从numpy转化成torch
new_numpy_tensor=pytorch_tensor.numpy(pytorch_tensor) 从torch转化成numpy

GPU tensor和CPU tensor之间的转换

推荐:
x=torch.randn(3,4) cpu的定义,3*4的矩阵
x_gpu=x.cuda(0) 把张量放到第1块GPU上
x_gpu=x.cuda(1) 把张量放到第2块GPU上
x_gpu=x.cuda() 把张量放到默认GPU上
x_gpu=x_gpu.cpu() 把在gpu的张量放到cpu上
x_array=x_gpu.cpu.numpy() 如果要把gpu的张量转化成numpy,要先把gpu的张量转化成cpu的张量

或者定义cuda数据类型
dtype = torch.cuda.FloatTensor # ???? GPU ? ????
gpu_tensor = torch.randn(10, 20).type(dtype)

Tensor的操作分为两种,一种是数学运算,一种是torch的高级操作。

数学运算

x=torch.ones(3,4) 3*4的矩阵,且数字都为1
y=torch.ones(3,4)
z=torch.ones(4,3)

k=x+3 在x的每个数字上+3
k=torch.add(x,3) 同上

x_sum=x+y 两个矩阵的加法
x_sum=torch.add(x,y)

x_mm=torch.mm(x,z) 两个矩阵相乘
x_mm=torch.matmul(x,z) 两个矩阵相乘

高级操作

x=torch.randn(4,3) 定义正态分布的随机的4*3矩阵
x.size()大小
x.shape
x.type() 数据类型
x.dim() 维度

将其转化为整形
x = x.long()
或者 x = x.type(torch.LongTensor)


x.view(3,4) 矩阵的重新排列
x.view(12)
x = x.view(-1, 4) -1表示任意的大小,4表示第二维变成5


torch.unsqueeze(x,dim=1) 给x维的第二维度增加一维
x=x.unsqueeze(1)
x.shape
squeeze是减维度,无论是降第一维度还是第二维度,都需要主要的是该维度上的数值大小必须等于1,否则无任务效果
unsqueeze增加维度时,没有等于1的限制。

pytorch中大多数的操作都支持inplace操作,也就是可以直接对tensor进行操作而不需要另外开辟内存空间。
直接对原始对象进行修改。
一般都是再操作的符号后面加_
比如
x = torch.ones(3, 3)
print(x.shape)
# unsqueeze 进行 inplace
x.unsqueeze_(0)
print(x.shape)
# transpose 进行 inplace
x.transpose_(1, 0) 转置
print(x.shape)

max_index,max_value=torch_max(x,dim=1) [行操作]沿着行取最大值
max_index,max_value=x.max(1)
torch.max(a,0) 返回每一列中最大值的那个元素,且返回索引(返回最大元素在这一列的行索引)
torch.max(a,1) 返回每一行中最大值的那个元素,且返回其索引(返回最大元素在这一行的列索引)
torch.max()[0], 只返回最大值的每个数
troch.max()[1], 只返回最大值的每个索引
torch.max()[1].data 只返回variable中的数据部分(去掉Variable containing:)
torch.max()[1].data.numpy() 把数据转化成numpy ndarry
torch.max()[1].data.numpy().squeeze() 把数据条目中维度为1 的删除掉
torch.max(tensor1,tensor2) element-wise 比较tensor1 和tensor2 中的元素,返回较大的那个值

sum_tensor=torch.sum(x,dim=1) [行操作]沿着行求和
sum_tensor=x.sum(1)

练习
创建一个float64,大小是3*2,随机初始化的tensor,将其转化成numpy的ndarray,输出其数据类型。

x=torch.randn(3,2)
x=x.type(torch.DoubleTensor)
x_array=x.numpy()
print(x_array.dtype)

Variable

为了构建神经网络,只使用Tensor不够,。
本质上Variable的操作与运算和Tensor是相同的。
唯一的区别是Variable有三个属性。

.data表示访问这个Variable中的tensor
.grad表示取得其中的梯度(相当于导数)
.grad_fn表示如何得到这个Variable

from torch.autograd import Variable
x=Variable(torch.ones(2,2),requires_grad=True) 是否进行梯度下降,是则True
y=Variable(torch.ones(2,2),requires_grad=True)
z=torch.sum(x+y) 在x+y后得到的矩阵,再把其中每个数值做求和工作
print(z,data)
z.backward() 对x,y分别求梯度
print(x.grad)
print(y.grad)

结果:

8
[torch.FloatTensor of size 1]
Variable containing:
1 1
1 1
[torch.FloatTensor of size 2*2]

自动求导AutoGrad
有更加广泛地应用,比如多次求导,对矩阵和向量的自动求导等等

from torch.autograd import Variable
x=Variable(torch.floatTensor([2]),requires_grad=True)
y=(x+2)**2
y.backward()
print(x.grad)

结果:

Variable containing:
8
[torch.FloatTensor of size 1]

复杂情况的自动求导

torch.ones_like(input, dtype=None, layout=None, device=None, requires_grad=False) → Tensor
返回一个填充了标量值1的张量,其大小与之相同 input。torch.ones_like(input)相当于 。
torch.ones(input.size(), dtype=input.dtype, layout=input.layout, device=input.device)
from torch.autograd import Variable
m=Variable(torch.FloatTensor([[2,3]]),requires_grad=True)#构建一个1*2的矩阵 [2 3]
n=torch.zeros(1,2)    #[0 0]
n[0,0]=m[0,0]**2
n[0,1]=m[0,1]**3
n.backward(torch.ones_like(n))#将(w0,w1)取成(1,1)
print(m.grad)#4 27

多次自动求导

from torch.autograd import Variable
x=Variable(torch.FloatTensor([3]),requires_grad=True)
y=x*2+x**2+3
print(y)
y.backward(retain_graph=True)#保留计算图
print(x.grad)#8
y.backward()#再对原先的y做一次求导
print(x.grad)#第一次的梯度和第二次的梯度加起来。8+8=16

练习

from torch.autograd import Variable
x=Variable(torch.FloatTensor([2,3]),requires_grad=True)
k=Variable(torch.zeros(2))
k[0]=x[0]**2+3*x[1]
k[1]=x[1]**2+2*x[0]
print(k)

j=torch.zeros(2,2)
k.backward(torch.FloatTensor([1,0]),retain_graph=True)
j[0]=x.grad.data

x.grad.data.zero_()#归零之前求的梯度
k.backward(torch.FloatTensor([0,1]))
j[1]=x.grad.data
print(j)

结果:

Variable containing:
13
13
[torch.FloatTensor of size 2]

4 3
2 6
[torch.FloatTensor of size 2x2]

神经网络

监督学习

非监督学习

强化学习

线性模型


梯度下降

调参就是调学习率。
输入数据->定义参数和模型->定义损失函数->反向传播,得到梯度->更新模型参数
示例:

import torch
import numpy as np
from torch.autograd import Variable
import matplotlib.pyplot as plt

torch.manual_seed(2017)#为CPU设置随机种子,输出<torch._C.Generator at 0x112959630>

#读入数据x和y
x_train=np.array([[3.3], [4.4], [5.5], [6.71], [6.93], [4.168],
                [9.779], [6.182], [7.59], [2.167], [7.042],
                [10.791], [5.313], [7.997], [3.1]], dtype=np.float32)
y_train = np.array([[1.7], [2.76], [2.09], [3.19], [1.694], [1.573],
                [3.366], [2.596], [2.53], [1.221], [2.827],
                [3.465], [1.65], [2.904], [1.3]], dtype=np.float32)
#画出图像
%matplotlib inline#%matplotlib具体作用是当你调用matplotlib.pyplot的绘图函数plot()进行绘图的时候,
## 或者生成一个figure画布的时候,可以直接在你的python console里面生成图像。

plt.plot(x_train,y_train,'bo')

#转化成Tensor
x_train=torch.from_numpy(x_train)
y_train=torch.from_numpy(y_train)

#定义参数w和b
w =Variable(torch.randn(1),requires_grad=True)#随机初始化
b=Variable(torch.zeros(1),requires_grad=True)#使用0进行初始化

#构建线性回归模型
x_train=Variable(x_train)
y_train=Variable(y_train)

def line_mode(x):
    return w*x+b

#试着看更新前的模型的输出是什么
#y_=line_mode(x_train)
#plt.plot(x_train.data.numpy(),y_train.data.numpy(),'bo',lable='real')
#plt.plot(x_train.data.numpy().y_.data.numpy(),'ro',lable='estimated')
#plt.legend()#加图例

def get_loss(y_,y):
    return torch.mean(y_-y)**2

#试着看更新前的loss是什么
#loss=get_loss(y_,y_train)
#print(loss)

for e in range(10):#10次更新
    y_=line_mode(x_train)
    loss=get_loss(y_,y_train)

    w.grad.data=0#归零梯度
    b.grad.data=0#归零梯度

    loss.backward()

    w.data=w.data-1e-2*w.grad.data#1e-2是学习率,更新w
    b.data=b.data-1e-2*b.grad.data#更新b

    print('epoch:{},loss:{}'.format(e.loss.data[0]))

#plt.plot(x_train.data.numpy(),y_train.data.numpy(),'bo',lable='real')
#plt.plot(x_train.data.numpy().y_.data.numpy(),'ro',lable='estimated')
#plt.legend()#加图例

多项式回归模型
函数是个三次的多项式
np.stack()函数解释

import torch
import numpy as np
from torch.autograd import Variable
import matplotlib.pyplot as plt

w_target=np.array([0.5,3,2.5])
b_target=np.array([0.9])

f_des='y={:.2f}+{:.2f}*x+{:.2f}*x^2+{:.2f}*x^3'.format(b_target[0],w_target[0],w_target[1],w_target[2])#打印出函数的式子
print(f_des)#y = 0.90 + 0.50 * x + 3.00 * x^2 + 2.40 * x^3

#画出这个函数的曲线
x_sample=np.arange(-3,3.1,0.1)
y_sample=b_target[0]+w_target[0]*x_sample+w_target[1]*x_sample**2+w_target[2]*x_sample**3
plt.plot(x_sample,y_sample,label='real curve')
plt.legend()
plt.show()

#构建数据x和y
#x是一个如下矩阵[x,x^2,x^3]
#y是函数的结果

x_train=np.stack([x_sample**i for i in range(1,4)],axis=1)#在行方面加一行
x_train=torch.from_numpy(x_train).float()#转成float tensor
y_train=torch.from_numpy(y_sample).float().unsqueeze(1)

#定义参数和模型
w=Variable(torch.randn(3,1),requires_need=True)
b=Variable(torch.zeros(1),requires_need=True)

#将x_train和y_train转化为Variable
x_train=Variable(x_train)
y_train=Variable(y_train)

def multi_line(x):
    return torch.mm(x,w)+b

def get_loss(y_,y):
    return torch.mean(y_,y)**2
#画出更新前的模型
#y_pred=multi_line(x_train)
#plt.plot(x_train.data.numpy()[:,0],y_pred.data.numpy(),color='r',lable='fitting curve')
#plt.plot(x_train.data.numpy()[:,0],y_sample,color='b',lable='real curve')
#plt.legend()
#plt.show()

for e in range(100):#进行100次更新
    y_pred=multi_line(x_train)
    loss=get_loss(y_pred,y_train)

    w.grad.data.zero_()
    b.grad.data.zero_()

    loss.backward()
    w.data=w.data-1e-3*w.grad.data
    b.data=b.data-1e-3*b.grad.data
    if (e+1)%20==0:
        print('epoch:{},Loss:{}'.format(e,loss.data[0]))

Logistic回归

预测


sigmoid函数


Loss函数

loss越小越好

PyTorch实现

y ∧ = σ ( w 1 x + w 2 y + b ) \mathop y\limits^ \wedge = \sigma ({w_1}x + {w_2}y+ b) y=σ(w1x+w2y+b)
loss=

定义sigmoid函数
def sigmoid(x):
    return 1/(1+np.exp(-x))
或者直接调用
import torch.nn.functional as F
F.sigmoid()
定义logistic回归模型
w=Variavle(torch.randn([2,1]),requires_need=True)
b=Variavle(torch.zeros(1),requires_need=True)
 
def logistic_regression(x):
    return F.sigmoid(torch.mm(x,w)+b)

def binary_loss(y_pred,y):
    logits=(y*y_pred.clamp(1e-12).log()+(1-y)*(1-y_pred).clamp(1e-12).log()).mean()
    return -logits

y_pred=logistic_regression(x_data)
loss=binary_loss(y_pred,y_data)

#自动求导并更新参数
loss.backward()
w.data=w.data-1e-1*w.grad.data
b.data=b.data-1e-1*b.grad.data

pytorch还有其他修正参数的方法。使用Optimizer更新
torch.optim
Parameter是Variable的一个子类,默认需要求梯度,而且能自动注册到PyTorch中 。

#使用torch.optim更新参数
from torch import nn
w=nn.Paramerer(torch.randn(2,1))
b=nn.Paramerer(torch.zeros(1))

def logistic_regression(x):
    return F.sigmoid(torch.mm(x,w)+b)

optimizer=torch.optim.SGD([w,b],lr=1.)#随机梯度下降

for e in range(1000):
    y_pred=logistic_regression(x_data)
    loss=binary_loss(y_pred,y_data)
    #更新参数
    optimizer.zero_grad()#使用优化器将梯度归0
    loss.backward()
    optimizer.step()#使用优化器来更新参数


更多的loss
nn.BCEWithLogitsLoss()是表示二分类法。

完整的Logistic回归模型实现

str.split(“o”)[0]得到的是第一个o之前的内容
str.split(“o”)[1]得到的是第一个o和第二个o之间的内容
str.split(“o”)[3]得到的是第三个o后和第四个o前之间的内容
str.split("[")[0]得到的是第一个 [ 之前的内容
filter()函数解释

y的由来?
首先理论上是这个样子的。
w0x0+w1x1+b=f(x)
f(x)最开始就设置为1, x1就是我们画图的y值。0是两个类别的分界处
所以: w0x0+w1x1+b=0 => y = (-b-w0*x)/w1

import torch
from torch.autograd import Variable
import numpy as np
import matplotlib.pyplot as plt
from torch import nn
import time

#%matplotlib inline

#设置随机种子,如果不加上这个函数的话,打印出来的随机数每次都不一样。
torch.manual_seed(2017)

with open('./data.txt','r') as f:#Linux系统
    data_list=[i.split('\n')[0].split(',') for i in f.readlines()]#从\n前开始划分,以,为分隔符
    data=[(float(i[0]),float(i[1]),float(i[3])) for i in data_list]

#标准化
x0_max=max([i[0] for i in data])
x1_max=max([i[1] for i in data])
data=[(i[0]/x0_max,i[1]/x0_max,i[2]) for i in data]

#画图
x0=list[filter(lambda x:x[-1]==0.0,data)]#第一类,最后一个元素为0
x1=list[filter(lambda x:x[-1]==1.0,data)]#第二类,最后一个元素为1

plot_x0=[i[0] for i in x0]
plot_y0=[i[1] for i in x0]
plot_x1=[i[0] for i in x1]
plot_y1=[i[1] for i in x1]

plt.plot(plot_x0,plot_y0,'ro',lable='x_0')#第一类
plt.plot(plot_x1,plot_y1,'bo',lable='x_1')#第二类
plt.legend(loc='best')#自动选择最不碍事的地返回放图例
plt.show()

#转化成NumPy,在转化成Tensor
np_data=np.array(data,dtype='float32')#转化成numpy array
x_data=torch.from_numpy(np.data[:,0:2])#转化成Tensor,大小为[100,2]
y_data=torch.from_numpy(np.data[:,-1]).unsqueeze(1)#转化成Tensor,大小为[100,1]

#定义sigmoid函数
def sigmoid(x):
    return 1/(1+torch.exp(-x))

#画出sigmoid的图像
plot_x=np.arange(-10,10.01,0.01)
plot_y=sigmoid(plot_x)
plt.plot(plot_x,plot_y,'r')
plt.show()

#转化为Variable
x_data=Variable(x_data)
y_data=Variable(y_data)

#参数与模型
"""
w=Variable(torch.randn(2,1),requires_need=True)
b=Variable(torch.zeros(1),requires_need=True)
"""
w=nn.Parameter(torch.randn(2,1))#用Parameter默认可自动更新参数
b=nn.Parameter(torch.zero(1))

def logistic_regression(x):
    return sigmoid(torch.mm(w,x)+b)

#画出参数更新前的结果
w0=w[0].data[0]
w1=w[1].data[0]
b0=b.data[0]

plt_x=np.arange(0.2,1,0.01 )
plt_y=-(b0-w0*plot_x)/w1
plt.plot(plot_x,plot_y,'b',lable='cutting line')
plt.plot(plot_x0,plot_y0,'ro',label='x_0')
plt.plot(plot_x1,plot_y1,'bo',lable='x_1')
plt.legend(loc='best')
plt.show()

def binary_loss(y_pred,y):
    logits=(y*y_pred.clamp(1e-12).log()+(1-y)*(1-y_pred).clamp(1e-12).log()).mean()
    return -logits

"""
y_pred=logistic_regression(x_data)
loss=binary_loss(y_pred,y_data)
print(loss)#0.6412

loss.backward()
w.data=w.data-0.1*w.grad.data
b.data=b.data-0.1*b.grad.data

y_pred=logistic_regression(x_data)
loss=binary_loss(y_pred,y_data)
print(loss)#0.6407
"""

#用torch.optim更新参数
optimizer=torch.optim.SGD([w,b],lr=1.)

start=time.time()#返回当前时间的时间戳(1970纪元后经过的浮点秒数)。
#进行1000次更新
for e in range(1000):
    #前向传播
    y_pred=logistic_regression(x_data)
    loss=binary_loss(y_pred,y_data)
    #反向传播
    optimizer.zero_grad()#使用优化器将梯度归零
    loss.backward()
    optimizer.step()#使用优化器来更新参数
    #计算正确率
    mask=y_pred.ge(0.5).float()
    acc=(mask==y_data).sum().data[0]/y_data.shape[0]
    if (e+1)%200==0:
        print('epoch:{},Loss:{:.5f},Acc:{:.5f}'.format(e+1,loss.data[0],acc))
        
during=time.time()-start
print()
print('During time:{:3f}s'.format(during))

#画出更新后的结果
w0=w[0].data[0]
w1=w[1].data[0]
b0=b.data[0]

plt_x=np.arange(0.2,1,0.01 )
plt_y=-(b0-w0*plot_x)/w1
plt.plot(plot_x,plot_y,'b',lable='cutting line')
plt.plot(plot_x0,plot_y0,'ro',label='x_0')
plt.plot(plot_x1,plot_y1,'bo',lable='x_1')
plt.legend(loc='best')
plt.show()

或者使用自带的loss

criterion=nn.BCEWithgLogitsLoss()#将sigmoid和loss写在一层,有更快的速度、更好的稳定性

w=nn.Parameter(torch.randn(2,1))#用Parameter默认可自动更新参数
b=nn.Parameter(torch.zero(1))

def logistic_reg(x):
	return torch.mm(x,w)+b

optimizer=torch.optim.SGD([w,b],lr=1.)
y_pred=logistic_reg(x_data)
loss=criterion(y_pred,y_data)
print(loss)

start=time.time()#返回当前时间的时间戳(1970纪元后经过的浮点秒数)。
#进行1000次更新
for e in range(1000):
    #前向传播
    y_pred=logistic_reg(x_data)
    loss=criterion(y_pred,y_data)
    #反向传播
    optimizer.zero_grad()#使用优化器将梯度归零
    loss.backward()
    optimizer.step()#使用优化器来更新参数
    #计算正确率
    mask=y_pred.ge(0.5).float()
    acc=(mask==y_data).sum().data[0]/y_data.shape[0]
    if (e+1)%200==0:
        print('epoch:{},Loss:{:.5f},Acc:{:.5f}'.format(e+1,loss.data[0],acc))

during=time.time()-start
print()
print('During time:{:3f}s'.format(during))

多层神经网络



激活函数(最右侧是Pytorch中调用)

激活函数的作用:

Pytorch实现


tanh ⁡ ( x ) = 2 σ ( 2 x ) − 1 \tanh (x) = 2\sigma (2x) - 1 tanh(x)=2σ(2x)1
R e L U ( x ) = max ⁡ ( 0 , x ) {\mathop{\rm Re}\nolimits} LU(x) = \max (0,x) ReLU(x)=max(0,x)
现在神经网络90%情况都是使用这个激活函数,简单而有效。
加快梯度下降法的收敛速度,计算简单。

from torch import nn
import torch,nn.funtional as F

这是常规的定义方法
#定义两层神经网络的参数
w1=nn.Parameter(torch.randn(2,4)*0.01)#隐藏层神经元个数2
b1=nn.Parameter(torch.zeros(4))

w2=nn.Parameter(torch.randn(4,1)*0.01)
b2=nn.Parameter(torch.zeros(1))

#定义模型
def two_network(x):
    x1=torch.mm(x,w1)+b1
    x1=F.tanh(x1)#pytorch自带的tanh激活函数
    x2=torch.mm(x1,w2)+b2
    return x2

optimizer=torch.optim.SGD([w1,w2,b1,b2],lr=1.)
criterion=nn.BCEWithLogitsLoss()

for e in range(10000):
    out=two_network(Variable(x))
    loss=criterion(out,Variable(y))
    
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    if(e+1)%1000==0:
        print('epoch:{},loss:{}'.format(e+1,loss.data[0]))

完整的代码实现:
np.r_中的r是row(行)的缩写,是按行叠加两个矩阵的意思,也可以说是按列连接两个矩阵,就是把两矩阵上下相加,要求列数相等,类似于pandas中的concat()。
np.c_中的c是column(列)的缩写,是按列叠加两个矩阵的意思,也可以说是按行连接两个矩阵,就是把两矩阵左右相加,要求行数相等,类似于pandas中的merge()。
np.r_和np.c_详解

import numpy as np
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = np.c_[a,b]
print(np.r_[a,b])#行
print(np.c_[a,b])#列

a = np.array([[1, 2, 3],[4,5,6]])
b = np.array([[0, 0, 0],[1,1,1]])
print("-------------------a------------------")
print(a)
print("-------------------b------------------")
print(b)
print("-------------------np.r_[a,b]--------------------")
print(np.r_[a,b])#行拼接
print("--------------------np.c_[a,b]-------------------")
print(np.c_[a,b])#列拼接

结果 :

[1 2 3 4 5 6]
[[1 4]
 [2 5]
 [3 6]]
-------------------a------------------
[[1 2 3]
 [4 5 6]]
-------------------b------------------
[[0 0 0]
 [1 1 1]]
-------------------np.r_[a,b]--------------------
[[1 2 3]
 [4 5 6]
 [0 0 0]
 [1 1 1]]
--------------------np.c_[a,b]-------------------
[[1 2 3 0 0 0]
 [4 5 6 1 1 1]]
import torch
import numpy as np
from torch import nn
from torch.autograd import Variable
import torch.nn.functional as F

import matplotlib.pyplot as plt
%matplotilb inline

def plot_decision_boundry(model,x,y):
    #set min and max values and give it some padding
    x_min,x_max=x[:,0].min()-1,x[:,0].max()+1
    y_min,y_max=x[:,1].min()-1,x[:,1].max()+1
    h=0.01
    #网格点,距离为h
    xx,yy=np.meshgrid(np.arange(x_min,x_max,h),np.arange(y_min,y_max,h))
    #ravel():扁平化,如果没有必要,不会产生源数据的副本,np.c_按列叠加
    #Predict the function value for the whole grid
    z=model(np.c_[xx.ravel(),yy.ravel()])
    z=z.reshape(xx.shape)
    #Plot the contour and training examples绘制轮廓和训练样例
    # xx和yy表示x轴方向的二维数据,yy表示y轴的二维方向的数据,Z表示颜色对应的标签值, cmap表示颜色板
    plt.contourf(xx,yy,z,cmap=plt.cm.Spectral)
    plt.ylable('x2')
    plt.xlable('x1')
    #散点图,c=颜色,s=大小
    #cmap = plt.cm.Spectral实现的功能是给label为1的点一种颜色,给label为0的点另一种颜色。
    plt.scatter(x[:,0],y[:,1],c=y.reshape(-1),s=40,cmp=plt.cm.Spectral)

np.random.seed(1)
m=400#样本数量
N=int(m/2)#每一类的点的个数,200
D=2#维数
x=np.zeros((m,D))
y=np.zeros((m,1),dtype='unit8')# label 向量,0为红色,1为蓝色
a=4

for j in range(2):
    ix=range(N*j,N*(j+1))
    t=np.linespace(j*3.12,(j+1)*3.12,N)+np.random.randn(N)*0.2#theta
    r=a*np.sin(4*t)+np.random.randn(N)*0.2
    x[ix]=np.c_[r*np.sin(t),r*np.cos(t)]
    y[ix]=j

plt.scatter(x[:,0],y[:,1],c=y.reshape(-1),s=40,cmp=nn.sm.Spectral)


"""
尝试用logistic回归来解决
"""
x=torch.from_numpy(x).float()
y=torch.from_numpy(y).float()

w=nn.Parameter(torch.randn(2,1))
b=nn.Parameter(torch.zeros(1))

def logistic_regression(x):
    return torch.mm(x,w)+b

optimizer=torch.optim.SGD([w,b],lr=1e-1)
criterion=nn.BCEWithLogitsLoss()

for e in range(100):
    out=logistic_regression(Variable(x))
    loss=criterion(out,Variable(y))

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    if(e+1)%20==0:
        print('epoch:{}.loss:{}'.format(e+1,loss.data[0]))

def plot_logistic(x):
    x=Variable(torch.from_numpy(x))
    out=F.sigmoid(logistic_regression(x))
    out=(out>0.5)*1
    return  out.data.numpy()

plot_decision_boundry(lambda x:plot_logistic(x),x.numpy(),y.numpy())
plt.title('logistic regression')

"""
定义两层神经网络的参数
"""
w1=nn.Parameter(torch.randn(2,4)*0.01)#隐藏神经元2个
b1=nn.Parameter(torch.zero(4))

w2=nn.Parameter(torch.randn(4,1)*0.01)#隐藏神经元2个
b2=nn.Parameter(torch.zero(1))

def two_networks(x):
    x1=torch.mm(x,w1)+b1
    x1=F.Tanh(x1)
    x2=torch(x1,w2)+b2
    return x2

optimizer=torch.optim.SGD([w1,w2,b1,b2],lr=1)
criterion=nn.BCEWithLogitsLoss()

for e in range(1000):
    out=two_networks(Variable(x))
    loss=criterion(out,Variable(y))

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    if (e+1)%200==0:
        print('epoch:{},loss:{}'.format(e+1,loss.data[0]))

def plot_network2(x):
    x=Variable(torch.from_numpy(x))
    x1=torch.mm(x,w1)+b1
    x1=F.tanh(x1)
    x2=torch.mm(x1,w2)+b2
    out=F.sigmoid(x2)
    out=(out>0.5)*1
    return out.data.numpy()

plot_decision_boundry(lambda x:plot_logistic(x),x.numpy(),y.numpy())
plt.title('2 layer network')

"""
nn.Sequential可以定义序列化的模型,比如上面的两层网络,
可以按照顺序进行定义
只能按照顺序执行
"""
#Sequential
seq_net=nn.Sequential(
		nn.linear(2,4)#pytorch中的线性层,wx+b
		nn.Tanh(),
		nn.linear(4,1))

#序列模块可以通过索引访问每一层
seq_net[0] #第一层

#打印出第一层的权重
w0=seq.net[0].weight
print(w0)

#通过parameter可以取得模型的参数
param=seq_net.parameters()

#定义优化器
optim=torch.optim.SGD(param,lr=1.)

for e in range(1000):
    out=seq_net(Variable(x))
    loss=criterion(out,Variable(y))

    optim.zero_grad()
    loss.backward()
    optim.step()
    if (e+1)%200==0:
        print('epoch:{},loss:{}'.format(e+1,loss.data[0]))

def plot_seq(x):
	out=(seq_net(Variable(torch.from_numpy(x).float())))
	out=(out>0.5)*1
	return out.data.numpy()

plot_decision_boundary(lambda x: plot_seq(x), x.numpy(), y.numpy())
plt.title('sequential')
"""
nn.Module
在后面还有具体解释
"""
class module_net(nn.Module):
	def __init__(self):
		super(module_net,self)__init__():
		#定义模型
		self.layer1=nn.Linear(2,4)
		self.layer2=nn.Tanh()
		self.layer3=nn.Linear(4,1)
	#任意定义网络顺序
	def forward(self,x):
		x=self.layer1(x)
		x=self.layer2(x)
		x=self.layer3(x)
	return x

如何保存模型

两种方式:
1.将模型结构和参数都保存在一起

#将参数和模型保存在一起
torch.save(seq_net,'save_seq_net.pth')

torch.save里面有两个参数,第一个是要保留的模型,第二个是保存的路径。
读取模型的方式

#读取保存的模型
seq_net=torch.load('save_seq_net.pth')
print(seq_net[0].weight)

结果:

seq_net1

Sequential(
(0): Linear(in_features=2, out_features=4)
(1): Tanh()
(2): Linear(in_features=4, out_features=1))

Parameter containing:
-0.5532 -1.9916
0.0446 7.9446
10.3188 -12.9290
10.0688 11.7754
[torch.FloatTensor of size 4x2]

2.只将参数保存下来

#保存模型参数
torch.save(seq_net.state_dict(),'save_seq_net_params.pth')

读取参数时需要重新定义一次模型

seq_net2=nn.Sequential(x)(
		nn.Linear(2,4),
		nn.Tanh(),
		nn.Linear(4,1))

seq_net2.load_state_dict(torch.load('save_seq_net_params.pth'))

print(seq.net2[0].weight)

结果:

seq_net2

Sequential(
(0): Linear(in_features=2, out_features=4)
(1): Tanh()
(2): Linear(in_features=4, out_features=1))

Parameter containing:
-0.5532 -1.9916
0.0446 7.9446
10.3188 -12.9290
10.0688 11.7754
[torch.FloatTensor of size 4x2]

推荐第二种,第二种可移植性更强。

nn.Module

class module_net(nn.module):
    def __init__(self,num_input,num_hidden,num_output):
        super(module_net,self).__init__()
        self.layer1=nn.Linear(num_input,num_hidden)
        self.layer2=nn.Tanh()
        self.layer3=nn.Linear(num_hidden,num_output)

    def fordward(self,x):
        x=self.layer1(x)
        x=self.layer2(x)
        x=self.layer3(x)
        return x

mo_net=module_net(2,4,1)

#访问模型中的某层可以直接通过名字

#第一层
l1=mo_net.layer1
print(l1)#Linear(in_features=2, out_features=4)
#打印第一层的权重
print(l1.weight)
#结果:
#Parameter containing:
#0.1492 0.4150
#0.3403 -0.4084
#-0.3114 -0.0584
#0.5668 0.2063
#[torch.FloatTensor of size 4x2]

#定义优化器
optim=torch.optim.SGD(mo_net.parameters(),lr=1.)

for e in range(1000):
    out=mo_net(Variable(x))
    loss=criterion(out,Variable(y))

    optim.zero_grad()
    loss.backward()
    optim.step()
    if (e+1)%200==0:
        print('epoch:{},loss:{}'.format(e+1,loss.data[0]))

#保存模型
torch.save(mo_net.state_dict,'save_mo_net_params.pth')

练习
定义一个5层甚至更深的模型,增加训练次数,改变学习率

net=nn.Squential(
    nn.Linear(2,10),
    nn.Tanh(),
    nn.Linear(10,10),
    nn.Tanh(),
    nn.Linear(10,10),
    nn.Tanh(),
    nn.Linear(10,1)
)
#定义优化器
optim=torch.optim.SGD(mo_net.parameters(),lr=0.1)

for e in range(2000):
    out=mo_net(Variable(x))
    loss=criterion(out,Variable(y))

    optim.zero_grad()
    loss.backward()
    optim.step()
    if (e+1)%200==0:
        print('epoch:{},loss:{}'.format(e+1,loss.data[0]))

#保存模型
torch.save(mo_net.state_dict,'save_mo_net_params.pth')

多分类问题&深层神经网络






c r o s s _ e n t r o p y ( p , q ) = E p [ − log ⁡ q ] cross\_entropy(p,q) = {E_p}[ - \log q] cross_entropy(p,q)=Ep[logq]
mnist输入
选一张图片
28*28 pixels=784 像素点
图片都是由像素点构成,计算机读取像素点作为一个矩阵。

DataLoader(dataset, batch_size=1, shuffle=False, sampler=None, 
num_workers=0, collate_fn=default_collate, pin_memory=False, 
drop_last=False)

dataset:加载的数据集(Dataset对象) 
batch_size:batch size 批量大小
shuffle::是否将数据打乱 
sampler: 样本抽样,后续会详细介绍 
num_workers:使用多进程加载的进程数,0代表不使用多进程 
collate_fn: 如何将多个样本数据拼接成一个batch,一般使用默认的拼接方式即可 
pin_memory:是否将数据保存在pin memory区,pin memory中的数据转到GPU会快一些 
drop_last:dataset中的数据个数可能不是batch_size的整数倍,drop_last为True会将多出来不足一个batch的数据丢弃
#定义数据集
#使用pytorch自带的DataLoader定义一个数据迭代器
train_data=DataLoader(train_set,batch_size=64,shuffle=True)
test_data=DataLoader(test_set,batch_size=128,shuffle=False)

#定义模型
#使用Sequential定义4层神经网络
net=nn.Sequential(
    nn.Linear(784,400),
    nn.ReLU(),
    nn.Linear(400, 200),
    nn.ReLU(),
    nn.Linear(200,100),
    nn.ReLU(),
    nn.Linear(100,10),
)
#定义loss函数和优化器
criterion=nn.CrossEntropyLoss()
optimizer=torch.optim.SGD(net.parameters(),lr=1e-1)#使用随机梯度下降,学习率0.1

#训练过程
for e in range(20):
    for im,lable in train_data:
        im=Varaible(im)
        label=Variable(label)
        #前向传播
        out=net(im)
        loss=criterion(out,label)
        #反向传播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

完整代码:

import numpy as np
import torch
from torchvision.datasets import mnist#导入pytorch内置的mnist数据
import matplotlib.pyplot as plt

from torch import nn
from torch.autograd import Varaible

from torch.utils.data import DataLoader

#使用内置函数下载mnist数据集
train_set=mnist.MNIST('./data',train=True,download=True)
test_set=mnist.MNIST('./data',train=False,download=True)

#看其中数据是什么样子
a_data,a_label=train_set[0]#a_data是数字图片,a_label是相应的数字标签
a_data=np.array(a_data,dtype='float32')
print(a_data.shape)#(28,28)
print(a_data)

#对于神经网络 ,第一层的输入是28*28=784,得到的数据需要使用reshape将它们拉平到一个一维向量

def data_tf(x):
    x=np.array(x,dtype(float32))/255
    x=(x-0.5)/0.5#标准化
    x=x.reshape((-1,))#拉平,reshape(-1),都是竖着的一列
    x=torch.from_numpy(x)
    return x

a,a_label=train_set[0]
print(a.shape)#torch.Size([784])
print(a_label)#数字标签

#重新载入数据集,申明定义的数据变换
train_set=mnist.MNIST('./data',train=True,transform=data_tf,download=True)
test_set=mnist.MNIST('./data',train=False,transform=data_tf,download=True)

#定义数据集
#使用pytorch自带的DataLoader定义一个数据迭代器
train_data=DataLoader(train_set,batch_size=64,shuffle=True)
test_data=DataLoader(test_set,batch_size=128,shuffle=False)

#打印出一个批次的数据大小
a,a_label=next(iter(train_data))
print(a.shape)#torch.Size([64,784])
print(a_label.shape)#torch.Size([64])
print(a_label)
#torch.Size([64, 784])
#tensor([8, 5, 8, 0, 1, 6, 8, 6, 4, 1, 9, 9, 7, 6, 5, 9, 6, 3, 1, 9, 0, 1, 8, 6,
#        8, 4, 2, 2, 4, 1, 2, 0, 1, 9, 7, 1, 8, 2, 0, 3, 2, 6, 7, 6, 4, 0, 9, 5,
#        2, 6, 4, 5, 3, 2, 7, 0, 2, 0, 1, 9, 3, 6, 0, 6])

#定义模型
#使用Sequential定义4层神经网络
net=nn.Sequential(
    nn.Linear(784,400),
    nn.ReLU(),
    nn.Linear(400, 200),
    nn.ReLU(),
    nn.Linear(200,100),
    nn.ReLU(),
    nn.Linear(100,10),
)
#定义loss函数和优化器
criterion=nn.CrossEntropyLoss()
optimizer=torch.optim.SGD(net.parameters(),lr=1e-1)#使用随机梯度下降,学习率0.1

#训练过程
losses=[]
acces=[]
eval_losses=[]
eval_acces=[]

for e in range(20):
    train_loss = 0
    train_acc = 0
    net.train()
    for im,lable in train_data:
        im=Varaible(im)
        label=Variable(label)
        #前向传播
        out=net(im)
        loss=criterion(out,label)
        #反向传播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        #记录误差
        train_loss+=loss.data[0]#每次是和64个真实标签的loss
        #计算分类的准确性
        _,pred=out.max(1)#矩阵中最大的数
        num_correct=(pred==label).sum().data[0]
        acc=num_correct/im.shape[0]#在torch中,64*784,每列64。实际784*64,每行64
        train_acc+=acc

    losses.append(train_loss/len(train_data))#平均每个batch_size的loss
    acces.append(train_acc/len(train_data))

    #在测试集上检验效果
    eval_loss=0
    eval_acc=0
    net.eval()#将模型改为预测模式
    for im,label in test_data:
        im=Variable(im)
        label=Variable(label)
        #前向传播
        out=net(im)
        loss=criterion(out,label)
        #记录误差
        eval_loss+=loss.data[0]
        #记录准确率
        _,pred=out.max(1)
        num_correct=(pred==label).sum().data[0]
        acc=num_correct/im.shape[0]
        eval_acc+=acc

    eval_losses.append(eval_loss/len(test_data))
    eval_acces.append(eval_acc / len(test_data))

    print('epoch:{},Train Loss:{:.6f},Train Acc:{:.6f},Eval Loss:{:.6f},Eval Acc:{:.6f}'
          .format(e,train_loss/len(train_data),train_acc/len(train_data),
            eval_loss / len(test_data),eval_acc / len(test_data)))

#画出loss曲线和准确率曲线
#%matplotlib inline
plt.title('train loss')
plt.plot(np.arange(len(losses)),losses)

plt.title('train acc')
plt.plot(np.arange(len(acces)),acces)

plt.title('eval loss')
plt.plot(np.arange(len(eval_losses)),eval_losses)

plt.title('eval acc')
plt.plot(np.arange(len(eval_acces)),eval_acces)

反向传播算法

图解






优化算法

除了梯度下降法之外,还有很多基于梯度下降法的改进算法:

局部最优解:局部最小点、鞍点。

"""
numpy.linspace(start, stop[, num=50[, endpoint=True[, retstep=False[, dtype=None]]]]])
返回在指定范围内的均匀间隔的数字(组成的数组),也即返回一个等差数列
start - 起始点,
stop - 结束点
num - 元素个数,默认为50,
endpoint - 是否包含stop数值,默认为True,包含stop值;若为False,则不包含stop值
retstep - 返回值形式,默认为False,返回等差数列组,若为True,则返回结果(array([`samples`, `step`])),
dtype - 返回结果的数据类型,默认无,若无,则参考输入数据类型。

plt.semilogx()和plt.semilogy()
用于绘制折线图,两个函数的 x 轴、y 轴分别是指数型的。
支持 plot 函数的所有参数,分别有一个重要参数:
basex: x 轴的底,需要大于 1
basey: y 轴的底,需要大于 1
"""
x_axis = np.linspace(0, 5, len(losses1), endpoint=True)
plt.semilogy(x_axis, losses1, label='batch_size=1')
plt.legend(loc='best')

随机梯度下降
batch size越小,梯度具有较高的随机性,从而震荡激烈。
batch size太大,对内存需要更高 ,同时不利于网络跳出局部极小点。
学习率太大会使得损失函数不断回跳,从而无法让损失函数较好降低,所以一般使用较小的学习率。

# 将速度初始化为和参数形状相同的张量
vs = []
for param in net.parameters():
vs.append(torch.zeros_like(param.data))
optimzier = torch.optim.SGD(net.parameters(), 1e-2)

动量法梯度下降
加完动量之后的loss下降的程度更低,
可以将动量理解成一种惯性作用,所以每次更新的幅度都会比不加动量的情况更多。

optimizer = torch.optim.SGD(net.parameters(), lr=1e-2, momentum=0.9) # 加动量

Adagrad
自适应

optimizer = torch.optim.Adagrad(net.parameters(), lr=1e-2)

RMSRrop
不同的alpha会使得loss在下降过程中的震荡程度不同。

optimizer = torch.optim.RMSprop(net.parameters(), lr=1e-3, alpha=0.9)

Adadelta
没有学习率

optimizer = torch.optim.Adadelta(net.parameters(), rho=0.9)

Adam
结合了动量法和RMSProp的优化算法

optimizer = torch.optim.Adam(net.parameters(), lr=1e-3)
参数初始化

一、使用NumPy来初始化
如果模型中的某一层需要被手动地修改

import numpy as np
import torch
from torch import nn

#定义一个Sequential模型
net1=nn.Sequential(
    nn.Linear(30.40),
    nn.ReLU(),
    nn.Linear(40,50),
    nn.ReLU(),
    nn.Linear(50,10)
)

#访问第一层的参数
w1=net1[0].weight
b1=net1[0].bias
print(w1.shape)#40*30

#定义一个Tensor直接对其进行替换
net1[0].weight.data=torch.from_numpy(np.random.uniform(3,5,size=(40,30)))#最小值为3,最大值为5
print(net1[0].weight)

模型中相同类型的层都需要初始化成相同的方式
用循环去访问:

for layer in net1:
    if isinstance(layer,nn.Linear):#判断是否为线性层,判断两个类型是否相同推荐使用 isinstance()。
        param_shape=layer.weight.shape
        layer.weight.data=torch.from_numpy(np.random.normal(0,0.5,size=param_shape))
        #定义为均值为0,方差为0.5的正态分布

二、使用Module进行参数初始化
如果相对其中某个层进行初始化,类似上面进行初始化即可。
但是,如果用循环方式访问,需要用两个属性:children和modules

import numpy as np
import torch
from torch import nn

class sim_net(nn.Module):
    def __init__(self):
        super(sim_net,self).__init__()
        self.l1=nn.Sequential(
            nn.Linear(30,40),
            nn.ReLU()
        )

        self.l1[0].weight.data=torch.randn(40,30)#直接对某一层初始化

        self.l2=nn.Sequential(
            nn.Linear(40,50),
            nn.ReLU()
        )

        self.l3 = nn.Sequential(
            nn.Linear(50, 10),
            nn.ReLU()
        )
    def forward(self,x):
        x=self.l1(x)
        x=self.l2(x)
        x=self.l3(x)
        return x


net2=sim_net()

#访问children
for i in net2.children():
    print(i)
"""
Sequential(
(0): Linear(in_features=30, out_features=40)
(1): ReLU()
)
Sequential(
(0): Linear(in_features=40, out_features=50)
(1): ReLU()
)
Sequential(
(0): Linear(in_features=50, out_features=10)
(1): ReLU()
)
"""
#访问module
for i in net2.modules():
    print(i)
"""
sim_net(
(l1): Sequential(
(0): Linear(in_features=30, out_features=40)
(1): ReLU()
)
(l2): Sequential(
(0): Linear(in_features=40, out_features=50)
(1): ReLU()
)
(l3): Sequential(
(0): Linear(in_features=50, out_features=10)
(1): ReLU()
)
)
Sequential(
(0): Linear(in_features=30, out_features=40)
(1): ReLU()
)
Linear(in_features=30, out_features=40)
ReLU()
Sequential(
(0): Linear(in_features=40, out_features=50)
(1): ReLU()
)
Linear(in_features=40, out_features=50)
ReLU()
Sequential(
(0): Linear(in_features=50, out_features=10)
(1): ReLU()
)
Linear(in_features=50, out_features=10)
ReLU()
"""

children只会访问到模型定义的第一层。上述在模型中定义了三个Sequential,所以只会访问到三个Sequential.
而modules访问的是每一层

for layer in net2.modules():
	if isinstance(layer, nn.Linear):
		param_shape = layer.weight.shape
		layer.weight.data = torch.from_numpy(np.random.normal(0, 0.5,size=param_shape))

三、对Tensor进行操作初始化torch.nn.init

from torch.nn import init

print(net1[0].weight)#40*30
init.xavier_uniform(net1[0].weight)
print(net1[0].weight)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值