pytorch学习
import torch
import numpy as np
a=torch.rand(4,3,28,28)
a[0].shape
torch.Size([3, 28, 28])
a[0,0].shape
torch.Size([28, 28])
a[0,0,2,4]
tensor(0.8694)
一、切片
1、连续采样
[:] #all
[:n] #从0开始到n
[n:] #从n开始到结束
[start:end] #开始的索引:结束的索引
a[:2].shape
torch.Size([2, 3, 28, 28])
a[:2,:1,:,:].shape
torch.Size([2, 1, 28, 28])
a[:2,1:,:,:].shape
torch.Size([2, 2, 28, 28])
a[:2,-1:,:,:].shape
torch.Size([2, 1, 28, 28])
2、 隔行采样
[start🔚采样步长step]
a[:,:,0:28:2,0:28:2].shape
torch.Size([4, 3, 14, 14])
a[:,:,::2,::2].shape
torch.Size([4, 3, 14, 14])
3、 通过具体索引采样
a.index_select(0,torch.tensor([0,2])).shape
torch.Size([2, 3, 28, 28])
a.index_select(1,torch.tensor([1,2])).shape
torch.Size([4, 2, 28, 28])
a.index_select(2,torch.arange(8)).shape
torch.Size([4, 3, 8, 28])
a.index_select(3,torch.arange(12)).shape
torch.Size([4, 3, 28, 12])
"…"仅仅是为了方便
a[...].shape
torch.Size([4, 3, 28, 28])
a[0,...].shape
torch.Size([3, 28, 28])
a[:,0,...].shape
torch.Size([4, 28, 28])
a[...,:2].shape
torch.Size([4, 3, 28, 2])
a[0,...,::2].shape
torch.Size([3, 28, 14])
二、维度变换
1、masked_select()
x = torch.randn(3,4)
x
tensor([[ 0.6480, 1.5947, 0.6264, 0.6051], [ 1.6784, 0.2768, -1.8780, -0.1133], [-0.6442, 0.8570, 0.1677, 0.2378]])
mask = x.ge(0.5)
mask
tensor([[ True, True, True, True], [ True, False, False, False], [False, True, False, False]])
torch.masked_select(x,mask)
tensor([0.6480, 1.5947, 0.6264, 0.6051, 1.6784, 0.8570])
torch.masked_select(x,mask).shape
torch.Size([6])
2、flatten index
src = torch.tensor([[4,3,5],[6,7,8]])
src
tensor([[4, 3, 5], [6, 7, 8]])
torch.take(src,torch.tensor([0,2,5]))
tensor([4, 5, 8])
3、view / reshape
a = torch.rand(4,1,28,28)
a.shape
torch.Size([4, 1, 28, 28])
a.view(4,28*28)
tensor([[0.6799, 0.1414, 0.2763, …, 0.1316, 0.1727, 0.5590], [0.4192, 0.5028, 0.3343, …, 0.0582, 0.3610, 0.0597], [0.7654, 0.6181, 0.8086, …, 0.7929, 0.1571, 0.3276], [0.5736, 0.3868, 0.4300, …, 0.5922, 0.5513, 0.7693]])
a.view(4,28*28).shape
torch.Size([4, 784])
a.view(4*1*28,28).shape
torch.Size([112, 28])
a.view(4*1,28,28).shape
torch.Size([4, 28, 28])
b = a.view(4,784)
b
tensor([[0.6799, 0.1414, 0.2763, …, 0.1316, 0.1727, 0.5590], [0.4192, 0.5028, 0.3343, …, 0.0582, 0.3610, 0.0597], [0.7654, 0.6181, 0.8086, …, 0.7929, 0.1571, 0.3276], [0.5736, 0.3868, 0.4300, …, 0.5922, 0.5513, 0.7693]])
b.view(4,28,28,1).shape
torch.Size([4, 28, 28, 1])
4、squeeze挤压 / unsqueeze展开
a.shape
torch.Size([4, 1, 28, 28])
a.unsqueeze(0).shape
torch.Size([1, 4, 1, 28, 28])
a.unsqueeze(-1).shape
torch.Size([4, 1, 28, 28, 1])
a.unsqueeze(4).shape
torch.Size([4, 1, 28, 28, 1])
a.unsqueeze(-4).shape
torch.Size([4, 1, 1, 28, 28])
a.unsqueeze(-5).shape
torch.Size([1, 4, 1, 28, 28])
a.unsqueeze(5).shape
IndexError Traceback (most recent call last) in ----> 1 a.unsqueeze(5).shape #超过了维度允许范围 IndexError: Dimension out of range (expected to be in range of [-5, 4], but got 5)
a = torch.tensor([1.2,2.3])
a
tensor([1.2000, 2.3000])
a.shape
torch.Size([2])
a.unsqueeze(-1)
tensor([[1.2000], [2.3000]])
a.unsqueeze(-1).shape
torch.Size([2, 1])
a.unsqueeze(0)
tensor([[1.2000, 2.3000]])
a.unsqueeze(0).shape
torch.Size([1, 2])
b = torch.rand(32)
b.shape
torch.Size([32])
f = torch.rand(4,32,14,14)
b = b.unsqueeze(1).unsqueeze(2).unsqueeze(0)
b.shape
torch.Size([1, 32, 1, 1])
经过上述步骤,把b由torch.Size([32]) 一维 变成了torch.Size([1, 32, 1, 1])四维
b.squeeze().shape
torch.Size([32])
b.squeeze(0).shape
torch.Size([32, 1, 1])
b.squeeze(-1).shape
torch.Size([1, 32, 1])
b.squeeze(1).shape
torch.Size([1, 32, 1, 1])
b.squeeze(-4).shape
torch.Size([32, 1, 1])
三、扩展: 下面两个效果一样
1、expand:broadcasting #只改变理解方式,不增加数据
a = torch.rand(4,32,14,14)
b = b.unsqueeze(1).unsqueeze(2).unsqueeze(0)
b.shape
torch.Size([1, 1, 1, 1, 32, 1, 1])
b = torch.rand(1,32,1,1)
b.expand(4,32,14,14).shape
torch.Size([4, 32, 14, 14])
b.expand(-1,32,-1,-1).shape
torch.Size([1, 32, 1, 1])
b.expand(-1,32,-1,-4).shape
torch.Size([1, 32, 1, -4])
2、repeat:memory copied #增加数据
b.shape
torch.Size([1, 32, 1, 1])
b.repeat(4,32,1,1).shape
torch.Size([4, 1024, 1, 1])
b.repeat(4,1,1,1).shape
torch.Size([4, 32, 1, 1])
b.repeat(4,1,32,32).shape
torch.Size([4, 32, 32, 32])
矩阵的转置 .t
a = torch.randn(3,4)
a.shape
torch.Size([3, 4])
a.t()
a.t().shape
torch.Size([4, 3])
transpose实现两两交换
a = torch.rand(4,3,32,32)
a1 = a.transpose(1,3).contiguous().view(4,3*32*32).view(4,3,32,32)
a2 = a.transpose(1,3).contiguous().view(4,3*32*32).view(4,32,32,3).transpose(1,3)
a1.shape,a2.shape
(torch.Size([4, 3, 32, 32]), torch.Size([4, 3, 32, 32]))
torch.all(torch.eq(a,a1))
tensor(False)
torch.all(torch.eq(a,a2))
tensor(True)
permute
a = torch.rand(4,3,28,28)
a.transpose(1,3).shape
torch.Size([4, 28, 28, 3])
b = torch.rand(4,3,28,32)
b.transpose(1,3).shape
torch.Size([4, 32, 28, 3])
b.transpose(1,3).transpose(1,2).shape
torch.Size([4, 28, 32, 3])
b.permute(0,2,3,1).shape
torch.Size([4, 28, 32, 3])
broadcast:扩展维度,扩张时不需要copy数据
1、如果前面没有维度,则需要在前面插入一个新的维度 2、把新加入的维度扩张成为相同的size。例:A,B。B没有内容,为B插入一个新维度,并把B扩张成和A相同的维度 3、经过卷积神经网络,生成feature maps:[4,32,14,14] 4、Bias:32,1,1=>1,32,1,1=>4,32,14,14
why broadcasting?
for actual demanding:
·regulation:[class,student,score],add bias for every students: +5score
·because [4,32,8]can only add with [4,32,8],
·on the contrary,we hope [4,32,8]can add with [5.0](0dimension tensor)
key:Match from last dim(从小维开始匹配)
situation 1:
·if current dim=1,expand to same
eg.A[4,32,8](four classes,32 students,8 lessons),now we want the score of each lesson be added 5.
B[1] => B[1,1,1] => B[4,32,8]
A[4,32,14,14]
B[1,32,1,1] => [4,32,14,14]
situation 2:
·if either has no dim,insert one dim and expand to same
now we just want the score of English be added 5
B[1,1,8] => [4,32,8] (through[0,0,5,0,0,0,0,0])
A[4,32,14,14]
B[14,14] => [1,1,14,14] =>[4,32,14,14]
situation 3:
·NOT broadcasting-able
A[4,32,14,14]
B[2,32,14,14]#dim 0 has distinct dim,NOT size 1,so cannot insert and expand to same
四、merge or split
1、cat在原维度上进行扩展
a = torch.rand(4,32,8)
b = torch.rand(5,32,8)
torch.cat([a,b],dim=0).shape
torch.Size([9, 32, 8])
2、stack会创建一个新的维度
a1 = torch.rand(32,8)
b1 = torch.rand(32,8)
c = torch.stack([a1,b1],dim=0)
c.shape
torch.Size([2, 32, 8])
torch.stack([a,b],dim=0).shape
RuntimeError Traceback (most recent call last) in ----> 1 torch.stack([a,b],dim=0).shape #要求a1,b1的shape完全相同 RuntimeError: stack expects each tensor to be equal size, but got [4, 32, 8] at entry 0 and [5, 32, 8] at entry 1
3、split按长度len进行拆分
c.shape
torch.Size([2, 32, 8])
aa,bb = c.split([1,1],dim=0)
aa.shape,bb.shape
(torch.Size([1, 32, 8]), torch.Size([1, 32, 8]))
aa,bb = c.split(1,dim=0)
aa.shape,bb.shape
(torch.Size([1, 32, 8]), torch.Size([1, 32, 8]))
aa,bb = c.split(2,dim=0)
ValueError Traceback (most recent call last) in ----> 1 aa,bb = c.split(2,dim=0) #长度一共才2,但是要把长度拆分成2,就无法拆分 ValueError: not enough values to unpack (expected 2, got 1)
4、chunk按数量进行拆分
c.shape
m,n = c.chunk(2,dim=0)
m.shape,n.shape
c.shape
五、基本运算
1、add / minus / multiply / divid
a = torch.rand(3,4)
a
b = torch.rand(4)
b
a+b
torch.add(a,b)
torch.all(torch.eq(a-b,torch.sub(a,b)))
torch.all(torch.eq(a*b,torch.mul(a,b)))
torch.all(torch.eq(a/b,torch.div(a,b)))
2、matmul
(1)2d tensor matmul
a = torch.tensor([[3.,3.],[3.,3.]])
a
b = torch.ones(2,2)
b
torch.mm(a,b)
torch.matmul(a,b)
a@b
a = torch.rand(4,784)
x = torch.rand(4,784)
w = torch.rand(512,784)
(x@w.t()).shape
#神经网络可以理解为tensor,矩阵的相乘相加
(2)>2d tensor matmul
a = torch.rand(4,3,28,64)
b = torch.rand(4,3,64,32)
a.shape,b.shape,torch.matmul(a,b).shape
b1 = torch.rand(4,1,64,32)
a.shape,b.shape,torch.matmul(a,b1).shape
b2 = torch.rand(4,64,32)
torch.matmul(a,b2)
3、power矩阵次方
a = torch.full([2,2],3.)
a.pow(2)
a**2
aa = a**2
aa.sqrt()
aa.rsqrt()
aa**(0.5)
tensor([[[0.3398, 0.9368, 0.4789, 0.9124, 0.6989, 0.3476, 0.5464, 0.8225], [0.6950, 0.3233, 0.8133, 0.8307, 0.9493, 0.7528, 0.7099, 0.3549], [0.9476, 0.4018, 0.5350, 0.4714, 0.2160, 0.7102, 0.8838, 0.9494], [0.8027, 0.9576, 0.7581, 0.9324, 0.9776, 0.7598, 0.6035, 0.6960], [0.8669, 0.9793, 0.3580, 0.8107, 0.7291, 0.7721, 0.6947, 0.9367], [0.9766, 0.3777, 0.4006, 0.9466, 0.7487, 0.9789, 0.9271, 0.7690], [0.1329, 0.9046, 0.7036, 0.3389, 0.9362, 0.6187, 0.3904, 0.8272], [0.7000, 0.8479, 0.9273, 0.7959, 0.3503, 0.4138, 0.8511, 0.7003], [0.6475, 0.6537, 0.4819, 0.4237, 0.4267, 0.3168, 0.9740, 0.3920], [0.4832, 0.6160, 0.8322, 0.6348, 0.1435, 0.6557, 0.9311, 0.5860], [0.6276, 0.8570, 0.9184, 0.8391, 0.6393, 0.4581, 0.9451, 0.4369], [0.7666, 0.7174, 0.9480, 0.9748, 0.6960, 0.9299, 0.8889, 0.1886], [0.7654, 0.5772, 0.4515, 0.8861, 0.3661, 0.4661, 0.2333, 0.7668], [0.5873, 0.6093, 0.9528, 0.1773, 0.7145, 0.6223, 0.2573, 0.6029], [0.6388, 0.9341, 0.9153, 0.6012, 0.6718, 0.7056, 0.2280, 0.6289], [0.6477, 0.7973, 0.9231, 0.6404, 0.5791, 0.6749, 0.8733, 0.7136], [0.8370, 0.2390, 0.2808, 0.9373, 0.9406, 0.2357, 0.6042, 0.4712], [0.4983, 0.6453, 0.8371, 0.8353, 0.6088, 0.7799, 0.5297, 0.3448], [0.9584, 0.8916, 0.8728, 0.7693, 0.6398, 0.7883, 0.1654, 0.6025], [0.6611, 0.1903, 0.9439, 0.7829, 0.9064, 0.7874, 0.5384, 0.5753], [0.9957, 0.4825, 0.5329, 0.7879, 0.8094, 0.8641, 0.7971, 0.7981], [0.9564, 0.6858, 0.8706, 0.7443, 0.9070, 0.6545, 0.6979, 0.9505], [0.1372, 0.0457, 0.9959, 0.1995, 0.9982, 0.5210, 0.6383, 0.9822], [0.8265, 0.6194, 0.8625, 0.9786, 0.9352, 0.1755, 0.5430, 0.6145], [0.3293, 0.5944, 0.4495, 0.8490, 0.3312, 0.8978, 0.4665, 0.6412], [0.9291, 0.6356, 0.9027, 0.8648, 0.3429, 0.8181, 0.5362, 0.8759], [0.8120, 0.3447, 0.7006, 0.7554, 0.2462, 0.2694, 0.7855, 0.7590], [0.9253, 0.5977, 0.7083, 0.8144, 0.9617, 0.7077, 0.6159, 0.9634], [0.6062, 0.7896, 0.9842, 0.4307, 0.8408, 0.5411, 0.3181, 0.6006], [0.8072, 0.7346, 0.6431, 0.0922, 0.5432, 0.7285, 0.2538, 0.4684], [0.9054, 0.9919, 0.9948, 0.9246, 0.8321, 0.1768, 0.8794, 0.2131], [0.8103, 0.9626, 0.8980, 0.6205, 0.5940, 0.6741, 0.5050, 0.9740]]])
4、Exp log
a = torch.exp(torch.ones(2,2))
a
tensor([[2.7183, 2.7183], [2.7183, 2.7183]])
torch.log(a)
tensor([[1., 1.],
[1., 1.]])
5、approximation近似解
.floor().ceil()
.round()
.trunc().frac()
a = torch.tensor(3.14)
a
tensor(3.1400)
a.floor(),a.ceil(),a.trunc(),a.frac()
(tensor(3.), tensor(4.), tensor(3.), tensor(0.1400))
a = torch.tensor(3.499)
a.round()
tensor(3.)
a = torch.tensor(3.5)
a.round()
tensor(4.)
6、clamp裁剪
gradient clipping梯度裁剪 (min) (min,max)
eg.for w in []
clamp(w.grad,10)
grad = torch.rand(2,3)*15
grad
tensor([[14.1900, 14.6534, 6.0197], [ 7.3536, 5.4166, 11.4699]])
grad.max()
tensor(14.6534)
grad.median()
tensor(7.3536)
grad.clamp(10)
tensor([[14.1900, 14.6534, 10.0000], [10.0000, 10.0000, 11.4699]])
grad
tensor([[14.1900, 14.6534, 6.0197], [ 7.3536, 5.4166, 11.4699]])
grad.clamp(0,10)
tensor([[10.0000, 10.0000, 6.0197], [ 7.3536, 5.4166, 10.0000]])
六、属性统计
1、norm范数
norm-p
a = torch.full([8],1.)
a,a.dtype
(tensor([1., 1., 1., 1., 1., 1., 1., 1.]), torch.float32)
b = a.view(2,4)
b,b.dtype
(tensor([[1., 1., 1., 1.], [1., 1., 1., 1.]]), torch.float32)
c = a.view(2,2,2)
c,c.dtype
输出:
(tensor([[[1., 1.],
[1., 1.]],
[[1., 1.],
[1., 1.]]]),
torch.float32)
a.norm(1),b.norm(1),c.norm(1)
(tensor(8.), tensor(8.), tensor(8.))
a.norm(2),b.norm(2),c.norm(2)
(tensor(2.8284), tensor(2.8284), tensor(2.8284))
b.norm(1,dim=1)
tensor([4., 4.])
b.norm(2,dim=1)
tensor([2., 2.])
c.norm(1,dim=0)
tensor([[2., 2.], [2., 2.]])
c.norm(2,dim=0)
tensor([[1.4142, 1.4142], [1.4142, 1.4142]])
2、min,max,mean,sum,prod
a = torch.arange(8).view(2,4).float()
a
tensor([[0., 1., 2., 3.], [4., 5., 6., 7.]])
a.min(),a.max(),a.mean(),a.sum(),a.prod()
(tensor(0.), tensor(7.), tensor(3.5000), tensor(28.), tensor(0.))
a.argmax(),a.argmin()
(tensor(7), tensor(0))
a.argmax(dim=0),a.argmin(dim=0)
(tensor([1, 1, 1, 1]), tensor([0, 0, 0, 0]))
a.argmax(dim=1),a.argmin(dim=1)
(tensor([3, 3]), tensor([0, 0]))
3、dim, keepdim
a.max(dim=1)
torch.return_types.max( values=tensor([3., 7.]), indices=tensor([3, 3]))
a.max(dim=1,keepdim=True)
torch.return_types.max( values=tensor([[3.], [7.]]), indices=tensor([[3], [3]]))
a.argmax(dim=1,keepdim=True)
tensor([[3], [3]])
4、top-k or k-th
.topk 最大的k个 largest kthvalue
a
tensor([[0., 1., 2., 3.], [4., 5., 6., 7.]])
a.topk(3,dim=1)
torch.return_types.topk( values=tensor([[3., 2., 1.], [7., 6., 5.]]), indices=tensor([[3, 2, 1], [3, 2, 1]]))
a.topk(3,dim=1,largest=False)
torch.return_types.topk( values=tensor([[0., 1., 2.], [4., 5., 6.]]), indices=tensor([[0, 1, 2], [0, 1, 2]]))
a.kthvalue(4,dim=1)
torch.return_types.kthvalue( values=tensor([3., 7.]), indices=tensor([3, 3]))
a.kthvalue(3)
torch.return_types.kthvalue( values=tensor([2., 6.]), indices=tensor([2, 2]))
a.kthvalue(2,dim=1)
torch.return_types.kthvalue( values=tensor([1., 5.]), indices=tensor([1, 1]))
5、compare
torch.eq(a,b)
torch.eaqul(a,b)
a>0,a.dtype
(tensor([[False, True, True, True], [ True, True, True, True]]), torch.float32)
torch.gt(a,0)
tensor([[False, True, True, True], [ True, True, True, True]])
a != 0
tensor([[False, True, True, True], [ True, True, True, True]])
b = torch.ones(2,3)
c = torch.randn(2,3)
b,c
(tensor([[1., 1., 1.], [1., 1., 1.]]), tensor([[-0.2182, -0.7458, -0.4226], [-0.0562, -1.0081, 0.9595]]))
torch.eq(b,c)
tensor([[False, False, False], [False, False, False]])
torch.equal(b,b)
True
七、高阶操作
where根据条件选取源头,可以替换不方便的循环逻辑控制
torch.where(condition,源头x,源头y)→tensor
return a tensor of elements selected from either x or y,depending on condition:
if condition,out_i = x_i,
otherwise,out_i = y_i
gather查表
torch.gather(input,dim,index,out=None)→tensor
gather values along an axis specified by dim.
for a 3-D tensor the output is specified by out[i][j][k]=input[i][j][index[i][j][k]]#if dim == 2
cond = torch.tensor([[0.6769,0.7271],[0.8884,0.4163]])
cond
tensor([[0.6769, 0.7271], [0.8884, 0.4163]])
a = torch.full([2,2],0.)
a
tensor([[0., 0.], [0., 0.]])
b = torch.full([2,2],1.)
b
tensor([[1., 1.], [1., 1.]])
torch.where(cond>0.5,a,b)
tensor([[0., 0.], [0., 1.]])
prob = torch.randn(4,10)
prob
tensor([[ 2.5846, 0.1205, 0.9951, -0.3636, 0.3576, 1.6804, -0.4364, -0.7844, 0.4014, 0.1721], [ 3.4028, -0.1766, -0.7051, 1.3496, -0.8652, -1.1937, -0.0648, -1.0526, -0.8368, -0.2342], [-1.9581, 1.1129, 1.2058, -1.0913, 1.5528, 0.6695, 1.0291, 1.4307, -1.2577, -0.7917], [-0.2095, 0.6609, -0.7099, 0.6236, -1.5831, 0.6597, 1.2883, -1.1838, -0.4961, -0.6793]])
idx = prob.topk(dim=1,k=3)
idx
torch.return_types.topk( values=tensor([[ 2.5846, 1.6804, 0.9951], [ 3.4028, 1.3496, -0.0648], [ 1.5528, 1.4307, 1.2058], [ 1.2883, 0.6609, 0.6597]]), indices=tensor([[0, 5, 2], [0, 3, 6], [4, 7, 2], [6, 1, 5]]))
idx = idx[1]
idx
tensor([[0, 5, 2], [0, 3, 6], [4, 7, 2], [6, 1, 5]])
label = torch.arange(10)+100
label
tensor([100, 101, 102, 103, 104, 105, 106, 107, 108, 109])
torch.gather(label.expand(4,10),dim=1,index=idx.long())
tensor([[100, 105, 102], [100, 103, 106], [104, 107, 102], [106, 101, 105]])
八、梯度
导数derivate 偏微分partial derivate 梯度gradient #把所有的偏微分看成向量
凸函数像碗一样
鞍点:在一个点处取到一个维度的极小值,另一个维度的极大值
影响因素:
initialization初始状态(初始位置) learning rate lr学习率(可以先调成0.001) escape minima 逃出局部最小值:添加动量(惯性)
梯度▽=(y对w的偏微分,y对b的偏微分)
1、激活函数
根据青蛙实验,借鉴生物神经元的生物机制,发现: 神经网络的输出结果并不是各个输入的简单求和,而是有一个阈值响应机制。 只有满足激活函数条件的值有输出结果。
机器学习中可用的激活函数只有六种:恒等函数、阶跃函数sign、sigmoid、tanh、ReLU、softmax。
softmax、恒等函数几乎不会出现在隐藏层上 sign、tanh几乎不会出现在输出层上 ReLU、sigmoid在隐藏层和输出层上应用广泛 激活函数(如果在中间层上,用h(z)表示;如果在输出层上,用g(z)表示)
.
h(z):激活函数一般指的是隐藏层上的,隐藏层的激活函数变动会影响输出结果。同一层上的激活函数应该保持一致,方便调试(也可以设置不同) g(z):输出层上的激活函数对输出结果无影响
(1)为了解决阶梯函数不可导的情况,科学家提出了连续的激活函数:sigmoid:
f(x) = 1/(1+e^(-x)) ·可导,对x的导数=f(x)-f2(x)=f(x)(1-f(x)),导数在0点最大 ·值域(0,1) ·缺点:当x越来越大,导数逐渐趋近于0,导数=f(x),就会出现函数长期得不到更新的现象,即梯度弥散(梯度消失)
·实际中,变量之间的关系通常都不是一条直线,而是呈现出某种曲线关系。为了让统计模型更好地拟合曲线,统计学家在线性回归方程的两边引入了联系函数(link function),对线性回归方程做各种变化,将变化后的方程称为“广义线性回归”。such as 等式两边同时取对数的对数函数回归;同时取指数的S形函数回归等。在这一过程中,统计学家注意到sigmoid函数带来的变化: ·sigmoid 可以化连续性变量为分类型变量(比如设置阈值为0.5,当σ>0.5时为1,σ<0.5时为0) ·将结果以几率(σ/(1-σ))的形式呈现,即可得出线性回归的z:ln(σ/(1-σ))=Xw。因为这个性质,在等号两边加sigmoid的算法被称为“对数几率回归”,在英文中就是logistic regression逻辑回归。逻辑回归是广义线性回归中最广为人知的算法,它是一个叫做“回归”(自变量是线性)实际上总是被用来做分类的算法。【σ是事件发生的概率,1-σ是事件不会发生的概率,σ/(1-σ)就是样本被预测为1的相对概率。当样本对应的σ越接近1或0,则认为逻辑回归对这个样本的预测结果越肯定,样本被分类正确的可能性也越高;如果σ非常接近阈值,就说明逻辑回归对这个样本究竟是哪个类别,不是非常肯定。】
z = torch.linspace(-100,100,10)
z
tensor([-100.0000, -77.7778, -55.5556, -33.3333, -11.1111, 11.1111, 33.3333, 55.5556, 77.7778, 100.0000])
torch.sigmoid(z)
tensor([0.0000e+00, 1.6655e-34, 7.4564e-25, 3.3382e-15, 1.4945e-05, 9.9999e-01, 1.0000e+00, 1.0000e+00, 1.0000e+00, 1.0000e+00])
(2)tanh 英文发音/tai chi/
·=2*sigmoid(2x)-1 ·多用于RNN中 ·值域(-1,1) ·中心点(0,0),自变量为0时导数最大
a = torch.linspace(-1,1,10)
a
tensor([-1.0000, -0.7778, -0.5556, -0.3333, -0.1111, 0.1111, 0.3333, 0.5556, 0.7778, 1.0000])
torch.tanh(a)
tensor([-0.7616, -0.6514, -0.5047, -0.3215, -0.1107, 0.1107, 0.3215, 0.5047, 0.6514, 0.7616])
(3)ReLU(Rectified Linear Unit)推荐优先使用,是神经网络领域中的宠儿 英文发音:/rel-you/
0,z<0 不响应,导函数为0 z,z>0 响应,导函数为1,不会放大和缩小,不会出现梯度弥散和梯度爆炸的现象 ·可以用于清除负元素 ·导函数是阶跃函数
a
tensor([-1.0000, -0.7778, -0.5556, -0.3333, -0.1111, 0.1111, 0.3333, 0.5556, 0.7778, 1.0000])
torch.relu(a)
tensor([0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.1111, 0.3333, 0.5556, 0.7778, 1.0000])
(4)Leaky ReLU
x<0 , y = ax (a很小,一般为0.02)
x>0 , y = x
self.model = nn.Sequential(
nn.Linear(784,200),
nn.LeakyReLU(inplace = True),
nn.Linear(200,200),
nn.LeakyReLU(inplace = True),
nn.Linear(200,10),
nn.LeakyReLU(inplace = True),
)
File “< ipython-input-150-8cfd5b39a69f>”, line 4 nn.LeakyReLU(inplace = True), ^ SyntaxError: invalid character in identifier
(5)SELU:解决ReLU在0处不连续的问题
(6)softplus:把ReLU在0处做平滑处理
(7)sign符号函数(在0点两侧符号相反),也称阶跃函数 1,z>0; 0,z=0; -1,z<0 #很少用
X = torch.tensor([[1,0,0],[1,1,0],[1,0,1],[1,1,1]],dtype=torch.float32)
andgate = torch.tensor([[0],[0],[0],[1]],dtype=torch.float32)
w = torch.tensor([-0.2,0.15,0.15],dtype=torch.float32)
def LinearRwithsign(X,w):
zhat = torch.mv(X,w)
andhat = torch.tensor([int(x) for x in zhat >= 0],dtype=torch.float32)
return zhat,andhat
zhat,andhat = LinearRwithsign(X,w)
zhat,andhat
(tensor([-0.2000, -0.0500, -0.0500, 0.1000]), tensor([0., 0., 0., 1.]))
九、tipical loss
基本优化思想
模型训练的目标:令神经网络的输出结果与真实值尽量接近。 模型训练: 1)定义基本模型,明确目标是求解权重向量w 2)定义损失函数/目标函数 (评估真实值与预测值差异的函数,以权重向量为自变量) ·求解损失函数需要用到复杂的数学工具: ·将损失函数L(w)转变成凸函数的数学方法,常见的有拉格朗日变换 ·在凸函数上求解L(w)的最小值对应的w的方法,也就是以梯度下降为代表的优化算法 对于回归类神经网络而言,最常见的损失函数是SSE= 3)定义优化算法 4)以最小化损失函数为目标,求解权重
损失函数:
(1)MSE【Mean squared error(均方误差)】:用于回归
·MSELoss = (1/m)∑(zi-zihat)2 = (L2_norm)2 = (1/m)*∑(y实际的值-yhat模型的输出值)2
·
(2)SSE【sum of the Squared Errors】:SSE = ∑(zi-zihat)2
from torch.nn import MSELoss
yhat = torch.randn(size=(50,),dtype=torch.float32)
y = torch.randn(size=(50,),dtype=torch.float32)
criterion = MSELoss()
loss = criterion(yhat,y)
loss
tensor(1.9280)
criterion = MSELoss(reduction = "sum")
loss = criterion(yhat,y)
loss
tensor(96.4017)
(3)Cross Entropy Loss(交叉熵),也叫对数损失:用于分类 熵表示“不确定性”
交叉熵H(p,q) = -∑p(x)logq(x)。
·当q越来越逼近于p时,H越来越接近0;
·当p=q,cross entropy值 = entropy值
·binary:二分类
极大似然估计求解:如果希望一件事发生,就应该增加这件事发生的概率,就只需要寻找其发生概率最大化的权重w。寻找相应的权重
w,使得目标事件的发生概率最大,就是极大似然估计的基本方法。
步骤:
①构筑似然函数P(w),用于评估目标事件发生的概率。该函数被设计成目标事件发生时,概率最大
②对整体似然函数取对数,构成对数似然函数lnP(w)
③在对数似然函数上对权重w求导,并使导数为0,对权重进行求解
P(yihat|xi,w) = P1^yi * P0^(1-yi),在数学上叫做逻辑回归的假设函数。yi表示样本的真实标签。这时模型的效果最好。
两边同时取ln,把连乘变成连加:lnP = ∑(yi*ln(σi) + (1-yi)*ln(1-σi))#σi指激活函数的输出结果
这就是二分类交叉熵损失函数。
求Loss希望求得极小值,而不是极大值。因此在前面加上“-”号:
L(w)i = -∑(yi*ln(σi) + (1-yi)*ln(1-σi)) #σi指激活函数的输出结果
只要求得最小值,就可以让模型在训练数据上拟合效果最好。具体求解需要用到优化算法。
·multi-class:多分类
深度学习基本都是:多分类交叉熵损失
多分类的标签不再服从0-1分布。σ指softmax函数的输出结果
交叉熵函数:L(w) = -∑yi_(k=j)ln(σi)。二分类交叉熵函数可以看成多分类交叉熵函数的一种特殊形式。
在求解时,取对数操作实在确定了似然函数后进行的,但从计算结果来看,对数操作只对softmax函数的结果σ起效。
因此在实际操作中,把ln(softmax(z))单独定义了一个功能:logsoftmax,pytorch中可以直接通过nn.logsoftmax类调用这个功能
同时,把对数之外的,乘以标签、加和、取负等过程打包起来,称为负对数似然函数(Negitive Log Likelihood function), 在pytorch中用nn.NLLLoss来进行调用
·+softmax:与softmax激活函数搭配使用
·softmax对张量进行放缩,每个值都是(0,1),且所有值加起来=1
·把数值转化成概率[2.0,1.0,0.1]→[0.7,0.2,0.1],把原来大的值放得更大,把原来小的压缩到比较密集的空间。原来2是1的两
倍,经过变化,0.7是0.2的3.5倍。
·leave it to logistic regression part
为什么对于分类问题不使用MSE?
sigmoid+MSE:会出现梯度消失的问题
交叉熵收敛速度更快
注:如果用交叉熵效果不好,可以使用MSE
m = 3*pow(10,3)
torch.random.manual_seed(420)
X = torch.rand((m,4),dtype=torch.float32)
w = torch.rand((4,1),dtype=torch.float32)
y = torch.randint(low=0,high=2,size=(m,1),dtype=torch.float32)
zhat = torch.mm(X,w)
sigma = torch.sigmoid(zhat)
sigma.shape
torch.Size([3000, 1])
loss = -(1/m)*sum(y*torch.log(sigma) + (1-y)*torch.log(1-sigma))
loss
tensor([0.7962])
m = 3*pow(10,6)
torch.random.manual_seed(420)
X = torch.rand((m,4),dtype=torch.float32)
w = torch.rand((4,1),dtype=torch.float32)
y = torch.randint(low=0,high=2,size=(m,1),dtype=torch.float32)
zhat = torch.mm(X,w)
sigma = torch.sigmoid(zhat)
import time
start1 = time.time()
loss1 = -(1/m)* sum (y*torch.log(sigma) + (1-y)*torch.log(1-sigma))
now1 = time.time()
print(now1 - start1)
18.511772871017456
start2 = time.time()
loss2 = -(1/m)*torch.sum(y*torch.log(sigma) + (1-y)*torch.log(1-sigma))
now2 = time.time()
print(now2 - start2)
0.03202319145202637
"""
由于交叉熵损失太常用了,因此在pytorch里面有专门的类用于计算,这里介绍两个nn模块中的类:
class BCEWithLogitsLoss
内置了sigmoid函数与交叉熵函数,它会自动计算输入值的sigmoid值。因此需要输入zhat与真实标签,且顺序不能变化,zhat必须在前。
class BCELoss
只有交叉熵函数,没有sigmoid层。因此需要输入sigma与真实标签,且顺序不能变化
这两个类都要求预测值与真实标签的数据类型(必须是浮点型)以及结构(shape)必须相同,否则运行就会报错。
"""
import torch.nn as nn
criterion1 = nn.BCELoss()
loss1 = criterion1(sigma,y)
criterion2 = nn.BCEWithLogitsLoss()
loss2 = criterion2(zhat,y)
loss1,loss2
(tensor(0.8685), tensor(0.8685))
"""
深度学习基本都是:多分类交叉熵损失
LogSoftmax + Negtive Log Likelihood function
"""
import torch
import torch.nn as nn
m = 3*pow(10,3)
torch.random.manual_seed(420)
X = torch.rand((m,4),dtype=torch.float32)
w = torch.rand((4,3),dtype=torch.float32)
y = torch.randint(low=0,high=3,size=(m,),dtype=torch.float32)
zhat = torch.mm(X,w)
logsm = nn.LogSoftmax(dim=1)
logsigma = logsm(zhat)
logsigma
tensor([[-1.1139, -0.8802, -1.3585], [-1.0558, -0.8982, -1.4075], [-1.0920, -1.0626, -1.1430], …, [-1.0519, -0.9180, -1.3805], [-1.0945, -1.1219, -1.0798], [-1.0276, -0.8891, -1.4649]])
criterion = nn.NLLLoss()
criterion(logsigma,y.long())
tensor(1.1147)
"""
更简便的方法:直接调用CrossEntropyLoss
criterion = nn.CrossEntropyLoss()
criterion(zhat,y.long())#只需要输入zhat
"""
criterion = nn.CrossEntropyLoss()
criterion(zhat,y.long())
tensor(1.1147)
重视展示网络结构和灵活性,应该使用不包含输出层激活函数的类: 通常在Model类中,__init__中层的数量与forward函数中对应的激活函数的数量是一致的。如果使用内置sigmoid/logsoftmax功能的类来计算损失函数, forward函数在定义时就会少一层(输出层),网络结构展示就不够简单明了。 对于结构复杂的网络而言,结构清晰就更为重要。 同时,如果激活函数是单独写的,要修改激活函数就变得很容易。如果混在损失函数中,要修改激活函数时就得改掉整个损失函数的代码,不利于维护。
重视稳定性和运算精度,使用包含输出层激活函数的类: 如果在一个Model中, 很长时间都不修改输出层的激活函数,考虑到模型的稳定运行,就使用内置了激活函数的类来计算损失函数。 同时,内置激活函数可以帮助推升运算的精度。
‘\n重视展示网络结构和灵活性,应该使用不包含输出层激活函数的类:\n通常在Model类中,__init__中层的数量与forward函数中对应的激活函数的数量是一致的。如果使用内置sigmoid/logsoftmax功能的类来计算损失函数,\nforward函数在定义时就会少一层(输出层),网络结构展示就不够简单明了。\n对于结构复杂的网络而言,结构清晰就更为重要。\n同时,如果激活函数是单独写的,要修改激活函数就变得很容易。如果混在损失函数中,要修改激活函数时就得改掉整个损失函数的代码,不利于维护。\n\n重视稳定性和运算精度,使用包含输出层激活函数的类:\n如果在一个Model中, 很长时间都不修改输出层的激活函数,考虑到模型的稳定运行,就使用内置了激活函数的类来计算损失函数。\n同时,内置激活函数可以帮助推升运算的精度。\n\n’
如何使用pytorch自动求导:
方法一:torch.autograd.grad(loss,[w1,w2,...])
[w1 grad,w2 grad...]
方法二:loss.backward()
调用方法:
w1.grad
w2.grad
1、熵:信息量之和,信息量越大,熵越大,事件越确定,越没有惊喜感
a = torch.full([4],1/4.)
a
tensor([0.2500, 0.2500, 0.2500, 0.2500])
-(a*torch.log2(a)).sum()
tensor(2.)
a = torch.tensor([0.1,0.1,0.1,0.7])
-(a*torch.log2(a)).sum()
tensor(1.3568)
a = torch.tensor([0.001,0.001,0.001,0.999])
-(a*torch.log2(a)).sum()
tensor(0.0313)
from torch.nn import functional as F
x = torch.randn(1,784)
w = torch.randn(10,784)
logits = x@w.t()
pred = F.softmax(logits,dim=1)
pred_log = torch.log(pred)
F.cross_entropy(logits,torch.tensor([3]))
tensor(16.1506)
F.nll_loss(pred_log,torch.tensor([3]))
tensor(16.1506)
2、多分类:把 softmax 放于输出层之前。
·在多分类中,神经元的个数与标签的个数是一致的。例如:如果是十分类,在输出层上就会存在十个神经元,分别输出十个不同的概率。此时样本的预测标签就是所有输出的概率中最大的概率对应的标签类别。
·softmax分子是多分类状况下 某一个 标签类别的回归结果的指数函数 分母是多分类状况下 所有 标签类别的回归结果的指数函数之和 softmax函数的结果代表了样本的结果为类别k的概率 例如:有三个分类:苹果,梨,百香果。样本i被分类为百香果的概率为:σ百香果 = e_z百香果 / (e_z苹果 + e_z梨 + e_z百香果)
由于softmax的分母和分子中都带有以e为底的指数函数,所以在计算中非常容易出现极大的数值。如果数字超出计算机处理室要求的有限数据宽度,计算机无法运算和表示,这种现象叫做“溢出”。
from torch.nn import functional as F
x = torch.ones(1)
w = torch.full([1],2.)
mse = F.mse_loss(torch.ones(1),x*w)
x,w,mse
(tensor([1.]), tensor([2.]), tensor(1.))
torch.autograd.grad(mse,[w])
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-161-e91b20e3e3cd> in <module>
----> 1 torch.autograd.grad(mse,[w]) #w在初始化时没有设置为需要导数信息,因此这里默认w不需要求导
D:\anaconda\lib\site-packages\torch\autograd\__init__.py in grad(outputs, inputs, grad_outputs, retain_graph, create_graph, only_inputs, allow_unused)
200 retain_graph = create_graph
201
--> 202 return Variable._execution_engine.run_backward(
203 outputs, grad_outputs_, retain_graph, create_graph,
204 inputs, allow_unused)
RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn
w.requires_grad_()
tensor([2.], requires_grad=True)
torch.autograd.grad(mse,[w])
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-163-3cc9e23d25a7> in <module>
----> 1 torch.autograd.grad(mse,[w])#这里报错的原因是w是动态图,也就是做一步更新一步。这里的图还是原来的图
D:\anaconda\lib\site-packages\torch\autograd\__init__.py in grad(outputs, inputs, grad_outputs, retain_graph, create_graph, only_inputs, allow_unused)
200 retain_graph = create_graph
201
--> 202 return Variable._execution_engine.run_backward(
203 outputs, grad_outputs_, retain_graph, create_graph,
204 inputs, allow_unused)
RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn
mse = F.mse_loss(torch.ones(1),x*w)
torch.autograd.grad(mse,[w])
(tensor([2.]),)
mse = F.mse_loss(torch.ones(1),x*w)
mse.backward()
w.grad
tensor([2.])
import torch
from torch.nn import functional as F
a = torch.rand(3)
a.requires_grad_()
tensor([0.0983, 0.3098, 0.4726], requires_grad=True)
p = F.softmax(a,dim=0)
p
tensor([0.2710, 0.3349, 0.3941], grad_fn=<SoftmaxBackward>)
p.backward()
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-168-40f625a2e25d> in <module>
----> 1 p.backward() # requires_grad=True只适用一次,第二次运行的时候就已经被清除掉了,需要重新设置
D:\anaconda\lib\site-packages\torch\tensor.py in backward(self, gradient, retain_graph, create_graph)
219 retain_graph=retain_graph,
220 create_graph=create_graph)
--> 221 torch.autograd.backward(self, gradient, retain_graph, create_graph)
222
223 def register_hook(self, hook):
D:\anaconda\lib\site-packages\torch\autograd\__init__.py in backward(tensors, grad_tensors, retain_graph, create_graph, grad_variables)
124
125 grad_tensors_ = _tensor_or_tensors_to_tuple(grad_tensors, len(tensors))
--> 126 grad_tensors_ = _make_grads(tensors, grad_tensors_)
127 if retain_graph is None:
128 retain_graph = create_graph
D:\anaconda\lib\site-packages\torch\autograd\__init__.py in _make_grads(outputs, grads)
48 if out.requires_grad:
49 if out.numel() != 1:
---> 50 raise RuntimeError("grad can be implicitly created only for scalar outputs")
51 new_grads.append(torch.ones_like(out, memory_format=torch.preserve_format))
52 else:
RuntimeError: grad can be implicitly created only for scalar outputs
p = F.softmax(a,dim=0)
torch.autograd.grad(p[1],[a],retain_graph=True)
(tensor([-0.0908, 0.2227, -0.1320]),)
torch.autograd.grad(p[2],[a])
(tensor([-0.1068, -0.1320, 0.2388]),)
(2)softmax溢出现象
z = torch.tensor([1010,100,999],dtype=torch.float32)
torch.exp(z)/torch.sum(torch.exp(z))
tensor([nan, nan, nan])
torch.softmax(z,0)
tensor([9.9998e-01, 0.0000e+00, 1.6701e-05])
z = torch.tensor([10,9,5],dtype=torch.float32)
torch.exp(z)/torch.sum(torch.exp(z))
tensor([0.7275, 0.2676, 0.0049])
torch.softmax(z,0)
tensor([0.7275, 0.2676, 0.0049])
torch.softmax(z,0).sum()
tensor(1.)
注:关于softmax( , )的维度参数
s = torch.tensor([[[1,2,4,5],[3,4,4,5],[5,6,4,5]],[[5,6,4,5],[7,8,4,5],[9,10,4,5]]],dtype=torch.float32)
s,s.shape
(tensor([[[ 1., 2., 4., 5.],
[ 3., 4., 4., 5.],
[ 5., 6., 4., 5.]],
[[ 5., 6., 4., 5.],
[ 7., 8., 4., 5.],
[ 9., 10., 4., 5.]]]),
torch.Size([2, 3, 4]))
s.shape[0],s.shape[1],s.shape[2]
(2, 3, 4)
s.shape[-3],s.shape[-2],s.shape[-1]
(2, 3, 4)
torch.softmax(s,dim=0)
tensor([[[0.0180, 0.0180, 0.5000, 0.5000],
[0.0180, 0.0180, 0.5000, 0.5000],
[0.0180, 0.0180, 0.5000, 0.5000]],
[[0.9820, 0.9820, 0.5000, 0.5000],
[0.9820, 0.9820, 0.5000, 0.5000],
[0.9820, 0.9820, 0.5000, 0.5000]]])
torch.softmax(s,dim=1)
tensor([[[0.0159, 0.0159, 0.3333, 0.3333],
[0.1173, 0.1173, 0.3333, 0.3333],
[0.8668, 0.8668, 0.3333, 0.3333]],
[[0.0159, 0.0159, 0.3333, 0.3333],
[0.1173, 0.1173, 0.3333, 0.3333],
[0.8668, 0.8668, 0.3333, 0.3333]]])
torch.softmax(s,dim=2)
tensor([[[0.0128, 0.0347, 0.2562, 0.6964],
[0.0723, 0.1966, 0.1966, 0.5344],
[0.1966, 0.5344, 0.0723, 0.1966]],
[[0.1966, 0.5344, 0.0723, 0.1966],
[0.2562, 0.6964, 0.0128, 0.0347],
[0.2671, 0.7262, 0.0018, 0.0049]]])
3、使用cross_entropy进行多分类实战
w1,b1 = torch.randn(200,784,requires_grad = True),torch.zeros(200,requires_grad = True)
w2,b2 = torch.randn(200,200,requires_grad = True),torch.zeros(200,requires_grad = True)
w3,b3 = torch.randn(10,200,requires_grad = True),torch.zeros(10,requires_grad = True)
def forward(x):
x = x@w1.t() + b1
x = F.relu(x)
x = x@w2.t() + b2
x = F.relu(x)
x = x@w3.t() + b3
x = F.relu(x)
return x
import torch
from torch import nn
from torch import optim
epochs = 10
optimizer = optim.SGD([w1,b1,w1,b2,w3,b3],lr = 1e-3)
criteon = nn.CrossEntropyLoss()
for epoch in range(epochs):
for batch_idx,(data,target) in enumerate(train_loader):
data = data.view(-1,28*28)
logits = forward(data)
loss = criteon(logits,target)
optimizer.zero_grad()
loss.backward()
optimizer.step()
4、二分类神经网络的原理及实现
(1)tensor实现二分类神经网络的正向传播
import torch
X = torch.tensor([[1,0,0],[1,1,0],[1,0,1],[1,1,1]],dtype=torch.float32)
X
tensor([[1., 0., 0.], [1., 1., 0.], [1., 0., 1.], [1., 1., 1.]])
andgate = torch.tensor([[0],[0],[0],[1]],dtype=torch.float32)
def LogisticR(X,w):
w = torch.tensor([-0.2,0.15,0.15],dtype=torch.float32)
zhat = torch.mv(X,w)
sigma = 1/(1 + torch.exp(-zhat))
andhat = torch.tensor([int(x) for x in sigma >= 0.5],dtype=torch.float32)
return sigma,andhat
sigma,andhat = LogisticR(X,w)
sigma,andhat
(tensor([0.4502, 0.4875, 0.4875, 0.5250]), tensor([0., 0., 0., 1.]))
andgate
tensor([0., 0., 0., 1.])
画图
(1)AND GATE
import matplotlib.pyplot as plt
import seaborn as sns
X = torch.tensor([[1,0,0],[1,1,0],[1,0,1],[1,1,1]],dtype=torch.float32)
plt.figure(figsize=(5,3))
plt.scatter(X[:,1],X[:,2]
,c=andgate
,cmap="rainbow"
)
plt.style.use('seaborn-whitegrid')
sns.set_style("white")
plt.title("AND GATE",fontsize=16)
plt.xlim(-1,3)
plt.ylim(-1,3)
plt.grid(alpha=.4,axis="y")
plt.gca().spines["top"].set_alpha(.0)
plt.gca().spines["right"].set_alpha(.0)
import numpy as np
x = np.arange(-1,3,0.5)
plt.plot(x,(0.23-0.15*x)/0.15
,color="k",linestyle="--");
二维平面中,任意一条线可以被表示为x1 = ax2 + b
变换一下,0 = b - x1 + ax2
0 = [1 - x1 + x2] * [b,-1,a].T
0 = Xw
X是特征张量,w是权重向量(默认为列向量)
当w,b固定时,直线就是固定的。
当w,b不固定时,直线就是任意一条直线。
在直线上方的点为0类(z <= 0),在直线下方的点为1类(z > 0)#可以用阶跃函数实现
具有分类功能的线叫做决策边界
(2)OR GATE
import torch
X = torch.tensor([[1,0,0],[1,1,0],[1,0,1],[1,1,1]],dtype=torch.float32)
orgate = torch.tensor([0,1,1,1],dtype=torch.float32)
def OR(X):
w = torch.torch.tensor([-0.08,0.15,0.15],dtype=torch.float32)
zhat = torch.mv(X,w)
yhat = torch.tensor([int(x) for x in zhat >= 0.5],dtype=torch.float32)
return orgate
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(5,3))
plt.scatter(X[:,1],X[:,2]
,c=orgate
,cmap="rainbow"
)
plt.style.use('seaborn-whitegrid')
sns.set_style("white")
plt.title("OR GATE",fontsize=16)
plt.xlim(-1,3)
plt.ylim(-1,3)
plt.grid(alpha=.4,axis="y")
plt.gca().spines["top"].set_alpha(.0)
plt.gca().spines["right"].set_alpha(.0)
import numpy as np
x = np.arange(-1,3,0.5)
plt.plot(x,(0.08-0.15*x)/0.15
,color="k",linestyle="--");
(3)NAND GATE
import torch
X = torch.tensor([[1,0,0],[1,1,0],[1,0,1],[1,1,1]],dtype=torch.float32)
nandgate = torch.tensor([1,1,1,0],dtype=torch.float32)
def NAND(X):
w = torch.torch.tensor([0.23,-0.15,-0.15],dtype=torch.float32)
zhat = torch.mv(X,w)
yhat = torch.tensor([int(x) for x in zhat >= 0],dtype=torch.float32)
return nandgate
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(5,3))
plt.scatter(X[:,1],X[:,2]
,c=nandgate
,cmap="rainbow"
)
plt.style.use('seaborn-whitegrid')
sns.set_style("white")
plt.title("NAND GATE",fontsize=16)
plt.xlim(-1,3)
plt.ylim(-1,3)
plt.grid(alpha=.4,axis="y")
plt.gca().spines["top"].set_alpha(.0)
plt.gca().spines["right"].set_alpha(.0)
import numpy as np
x = np.arange(-1,3,0.5)
plt.plot(x,(0.23-0.15*x)/0.15
,color="k",linestyle="--");
import torch
X = torch.tensor([[1,0,0],[1,1,0],[1,0,1],[1,1,1]],dtype=torch.float32)
xorgate = torch.tensor([0,1,1,0],dtype=torch.float32)
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize=(5,3))
plt.scatter(X[:,1],X[:,2]
,c=xorgate
,cmap="rainbow"
)
plt.title("XOR GATE",fontsize=16)
plt.xlim(-1,3)
plt.ylim(-1,3)
plt.grid(alpha=.4,axis="y")
plt.gca().spines["top"].set_alpha(.0)
plt.gca().spines["right"].set_alpha(.0)
sigma_nand = NAND(X)
sigma_nand
tensor([1., 1., 1., 0.])
sigma_or = OR(X)
sigma_or
tensor([0., 1., 1., 1.])
x0 = torch.tensor([1,1,1,1],dtype=torch.float32)
x0
tensor([1., 1., 1., 1.])
input_2 = torch.cat((x0.view(4,1),sigma_nand.view(4,1),sigma_or.view(4,1)),dim=1)
input_2
tensor([[1., 1., 0.], [1., 1., 1.], [1., 1., 1.], [1., 0., 1.]])
import torch
X = torch.tensor([[1,0,0],[1,1,0],[1,0,1],[1,1,1]],dtype=torch.float32)
andgate = torch.tensor([0,0,0,1],dtype=torch.float32)
def AND(X):
w = torch.torch.tensor([-0.2,0.15,0.15],dtype=torch.float32)
zhat = torch.mv(X,w)
andhat = torch.tensor([int(x) for x in zhat >= 0],dtype=torch.float32)
return andhat
AND(input_2)
tensor([0., 1., 1., 0.])
5、GPU加速
device = torch.device('cuda:0')
net = MLP().to(device)
optimizer = optim.SGD([net.parameters()],lr = learning_rate)
criteon = nn.CrossEntropyLoss().to(device)
for epoch in range(epochs):
for batch_idx,(data,target) in enumerate(train_loader):
data = data.view(-1,28*28)
data,target = data.to(device),target.cuda()
十、链式法则
前向传播:求导之前要先向右计算一次所有的变量值
1、单层感知机:
(1)单层(只有1层输出的)线性回归神经网络:(特征输入) 输入信号 > 人工神经元 > 信号输出 (预测结果输出)
神经网络的层数不包括“输入层”
zhat(z的预测结果) = wi * xi + b。用矩阵表示:zhat = Xw(x中为1的那一列是专门用来和权重相乘的) 为什么是z不是y?在深度学习中,y永远表示标签。因为深度学习的输出不是标签,所以不用y。通常用粗体的小写字母表示列向量,粗体的大写字母表示矩阵或行列式。在机器学习中,默认所有的一维向量都是列向量。
线性回归的任务:拟合。 预测函数的本质是需要构建的模型,预测函数的核心是找出模型的参数向量w 把竖着排列的称为一层,一个神经元上只有一个特征,神经元的个数=特征数+1(1指偏差对应的神经元)。输出层至少有一个神经元。连接的线称为轴突,
x = torch.randn(1,10)
w = torch.randn(1,10,requires_grad = True)
o = torch.sigmoid(x@w.t())
o.shape
torch.Size([1, 1])
loss = F.mse_loss(torch.ones(1,1),o)
loss.shape
torch.Size([])
loss.backward()
w.grad
tensor([[-0.1313, 0.0248, 0.0298, -0.0075, -0.0821, 0.0185, 0.0354, 0.0332, -0.0105, 0.0642]])
例:算出z的结果。z=b + w1x1 + w2x2
import torch
X = torch.tensor([[1,0,0],[1,1,0],[1,0,1],[1,1,1]]
,dtype = torch.float32)
w = torch.tensor([-0.2,0.15,0.15])
X,w
(tensor([[1., 0., 0.], [1., 1., 0.], [1., 0., 1.], [1., 1., 1.]]), tensor([-0.2000, 0.1500, 0.1500]))
def LinearR(X,w):
zhat = torch.mv(X,w)
return zhat
zhat = LinearR(X,w)
zhat
tensor([-0.2000, -0.0500, -0.0500, 0.1000])
torch.Tensor([[1,0,0],[1,1,0],[1,0,1],[1,1,1]])
tensor([[1., 0., 0.], [1., 1., 0.], [1., 0., 1.], [1., 1., 1.]])
tensor与Tensor的区别:
torch.tensor会先判断输入的数据类型,然后根据其确定tensor中的数据类型 torch.Tensor无论输入数据是什么,都会无脑输出float32
z = torch.tensor([-0.2000, -0.0500, -0.0500, 0.1000])
z.ndim
1
zhat == z
tensor([ True, False, False, False])
zhat-z
tensor([0.0000e+00, 3.7253e-09, 3.7253e-09, 7.4506e-09])
SSE = sum((zhat - z)**2)
SSE
tensor(8.3267e-17)
torch.set_printoptions(precision=30)
zhat,z
(tensor([[0.695239484310150146484375000000],
[0.566381037235260009765625000000],
[1.094140648841857910156250000000],
[0.965282201766967773437500000000]], grad_fn=<AddmmBackward>),
tensor([-0.200000002980232238769531250000, -0.050000000745058059692382812500,
-0.050000000745058059692382812500, 0.100000001490116119384765625000]))
preds = torch.ones(300,68,64,64)
preds.shape
torch.Size([300, 68, 64, 64])
a = 300*68*64*64
a
83558400
preds = torch.ones(300,68,64,64) * 0.1
preds.sum() * 10
tensor(83558328.)
preds = torch.ones(300,68,64,64)
preds.sum()
tensor(83558400.)
torch.allclose(zhat,z)
True
(2)torch.nn.Linear(表示输出层,是nn.modual大类下的子类)实现单层回归神经网络的正向传播
使用时不需要定义w,b。
输入层由特征矩阵决定,所以在神经网络中不需要定义输入层,所以看一个网络有几层时,不算输入层。
用这种方法生成的z只是和 特征矩阵 结构相似的随机数,还没有告诉神经网络去预测正确结果
torch.set_printoptions(precision=4)
X = torch.tensor([[0,0],[1,0],[0,1],[1,1]],dtype=torch.float32)
X
tensor([[0., 0.], [1., 0.], [0., 1.], [1., 1.]])
output = torch.nn.Linear(2,1)
zhat = output(X)
zhat
tensor([[ 0.1788], [-0.0734], [ 0.1401], [-0.1121]], grad_fn=)
output.weight,output.bias
(Parameter containing: tensor([[-0.2522, -0.0387]], requires_grad=True), Parameter containing: tensor([0.1788], requires_grad=True))
torch.random.manual_seed(420)
output = torch.nn.Linear(2,1)
zhat = output(X)
zhat,zhat.shape
(tensor([[0.6730], [1.1048], [0.2473], [0.6792]], grad_fn=), torch.Size([4, 1]))
(3)torch.functional实现单层二分类神经网络的正向传播 => 也就是逻辑回归
逻辑回归与线性回归唯一的区别就是:在线性回归的结果之后套上了sigmoid函数
只要让nn.Linear输出结果再经过sigmoid函数,就可以实现逻辑回归的正向传播了
#在nn.module里调出来的都是类,在nn.functional里调出来的全是函数
import torch
from torch.nn import functional as F
X = torch.tensor([[0,0],[1,0],[0,1],[1,1]],dtype=torch.float32)
torch.random.manual_seed(420)
dense = torch.nn.Linear(2,1)
zhat = dense(X)
sigma = F.sigmoid(zhat)
y = [int(x) for x in sigma >= 0.5]
y
[1, 1, 1, 1]
zhat,torch.sign(zhat)
(tensor([[0.6730], [1.1048], [0.2473], [0.6792]], grad_fn=), tensor([[1.], [1.], [1.], [1.]], grad_fn=))
F.relu(zhat)
tensor([[0.6730], [1.1048], [0.2473], [0.6792]], grad_fn=)
torch.tanh(zhat)
tensor([[0.5869], [0.8022], [0.2424], [0.5910]], grad_fn=)
2、多层感知机:
x = torch.randn(1,10)
w = torch.randn(2,10,requires_grad = True)
o = torch.sigmoid(x@w.t())
o.shape
torch.Size([1, 2])
loss = F.mse_loss(torch.ones(1,2),o)
loss
tensor(0.5097, grad_fn=)
loss.backward()
w.grad
tensor([[ 0.0167, 0.0093, 0.0218, -0.0144, 0.0156, -0.0078, -0.0311, -0.0236, 0.0132, 0.0180], [ 0.0020, 0.0011, 0.0026, -0.0017, 0.0019, -0.0009, -0.0037, -0.0028, 0.0016, 0.0022]])
w.grad.shape
torch.Size([2, 10])
神经网络是黑箱
从左往右的过程是神经网络的正向传播过程。左边的层叫“上层”,右边的层叫“下层”。输入是第0层,第一个隐藏层称为第一层。
神经元从上往下编号,1,2,……。
不同神经元的区别在于加和之后使用的激活函数(如果在中间层上,用h(z)表示;如果在输出层上,用g(z)表示)。z是加和的结果,σ是加和之后所经过激活函数之后的结果。 z和σ位于神经元上,w和b位于层之间连接处。
神经网络非常复杂,要弄清楚每个w,b是不可能的。 所以深度学习不太看重可解释性,因为我们是不知道神经网络是怎么做的。要想从最后的结果追溯到输入的特征是非常困难的。
单层神经网络的w是一个列向量,但是在多层神经网络中,W是矩阵,因为有多层,多组w取值。 Z要和特征标签顺序一致。为了让Z保持列向量,所以需要把W写在X前面,也就是列行,n_featuresn_samples
真正让神经网络拥有处理线性和非线性问题的能力的,是隐藏层上的激活函数。如果没有激活函数,输出结果就 只是做了仿射变换,依然是线性变换,不能解决非线性问题。可见,“层”本身不是解决神经网络的非线性问题的关键, 层上的h(z)才是。如果h(z)是线性函数或者不存在,那增加再多的层也没用。
可以尝试把上述AND,OR,NAND的激活函数注释掉,或换成另一个激活函数,观察输出结果。
从0实现深度神经网络的正向传播:
"""
题目:假设有500条数据,20个特征,标签分为3分类。
现在要实现一个三层神经网络:第一层有13个神经元,第二层有8个神经元,第三层是输出层。
其中第一层的激活函数是relu,第二层的激活函数是sigmoid。
实现代码如下:
"""
import torch
import torch.nn as nn
from torch.nn import functional as F
torch.random.manual_seed(420)
x = torch.rand((500,20),dtype=torch.float32)
y = torch.randint(low=0,high=3,size=(500,1),dtype=torch.float32)
class Model(nn.Module):
def __init__(self,in_features=10,out_features=2):
"""
用于定义class Model这个类本身。nn.Module这个类有几千行代码,其中init函数代码就占了大多数。
括号里第一个参数是固定不变的self,表示这个类本身。
in_features表示输入该神经网络的特征数(输入层上的神经元数目),out_features表示该神经网络的输出的特征数(输出层上的神经元数目)。
"""
super(Model,self).__init__()
"""
super帮助继承父类更多的细节。
需要继承的子模块名称[查找Model的父类模块nn.Model,把nn.model中的__init__全部复制到子类的__init__中。]
如果没有super这一行,子类就无法继承父类的init函数,下面神经网络的各个层也就不能定义了。
"""
self.Linear1 = nn.Linear(in_features,13,bias=True)
self.Linear2 = nn.Linear(13,8,bias=True)
self.output = nn.Linear(8,out_features,bias=True)
def forward(self,x):
z1 = self.Linear1(x)
sigma1 = torch.relu(z1)
z2 = self.Linear2(sigma1)
sigma2 = torch.sigmoid(z2)
z3 = self.output(sigma2)
sigma3 = F.softmax(z3,dim=1)
return sigma3
x.shape
torch.Size([500, 20])
x.shape[1],y.unique()
(20, tensor([0., 1., 2.]))
input_ = x.shape[1]
output_ = len(y.unique())
input_,output_
(20, 3)
torch.random.manual_seed(420)
net = Model(in_features=input_,out_features=output_)
net.forward(x).shape
torch.Size([500, 3])
net.Linear1.weight.shape
torch.Size([13, 20])
net.Linear2.weight.shape
torch.Size([8, 13])
net.output.weight.shape
torch.Size([3, 8])
net.Linear1.bias.shape
torch.Size([13])
super函数
class FooParent(object):
def __init__(self):
self.parent = 'PARENT!!'
print('Running __init__,I am parent')
def bar(self,message):
self.bar = "This is bar"
print("%s from Parent" % message)
FooParent()
Running __init__,I am parent
<__main__.FooParent at 0x1f35c5da6a0>
FooParent().parent
Running __init__,I am parent
'PARENT!!'
class FooChild(FooParent):
def __init__(self):
print("Running __init__,I am child")
FooChild().bar("HAHAHA")
Running __init__,I am child
HAHAHA from Parent
FooChild().parent
Running __init__,I am child
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-46-1aa4e3354ad7> in <module>
----> 1 FooChild().parent #子类没有继承父类的__init__中定义的属性
AttributeError: 'FooChild' object has no attribute 'parent'
class FooChild(FooParent):
def __init__(self):
super(FooChild,self).__init__()
print("I am running __init__,I am child")
FooChild().parent
Running __init__,I am parent
I am running __init__,I am child
'PARENT!!'
net.training
True
def initial_0(m):
print(m)
if type(m) == nn.Linear:
m.weight.data.fill_(0)
print(m.weight)
net.apply(initial_0)
Linear(in_features=20, out_features=13, bias=True)
Parameter containing:
tensor([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]],
requires_grad=True)
Linear(in_features=13, out_features=8, bias=True)
Parameter containing:
tensor([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]],
requires_grad=True)
Linear(in_features=8, out_features=3, bias=True)
Parameter containing:
tensor([[0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0.]], requires_grad=True)
Model(
(Linear1): Linear(in_features=20, out_features=13, bias=True)
(Linear2): Linear(in_features=13, out_features=8, bias=True)
(output): Linear(in_features=8, out_features=3, bias=True)
)
Model(
(Linear1): Linear(in_features=20, out_features=13, bias=True)
(Linear2): Linear(in_features=13, out_features=8, bias=True)
(output): Linear(in_features=8, out_features=3, bias=True)
)
net.parameters()
<generator object Module.parameters at 0x000001F35C6174A0>
for param in net.parameters():
print(param)
Parameter containing:
tensor([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]],
requires_grad=True)
Parameter containing:
tensor([ 1.3508e-01, 1.5439e-01, -1.9350e-01, -6.8777e-02, 1.3787e-01,
-1.8474e-01, 1.2763e-01, 1.8031e-01, 9.5152e-02, -1.2660e-01,
1.4317e-01, -1.4945e-01, 3.4258e-05], requires_grad=True)
Parameter containing:
tensor([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]],
requires_grad=True)
Parameter containing:
tensor([ 0.0900, -0.0597, 0.0268, -0.0173, 0.0065, 0.0228, -0.1408, 0.1188],
requires_grad=True)
Parameter containing:
tensor([[0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0.]], requires_grad=True)
Parameter containing:
tensor([-0.0156, -0.0730, -0.2637], requires_grad=True)
3、链式法则 :通过乘积完成对比较复杂的梯度的求解
ay/ax = (ay/au) * (au/ax)
x = torch.tensor(1.)
w1 = torch.tensor(2.,requires_grad = True)
b1 = torch.tensor(1.)
w2 = torch.tensor(2.,requires_grad = True)
b2 = torch.tensor(1.)
y1 = x*w1+b1
y2 = y1*w2+b2
dy2_dy1 = torch.autograd.grad(y2,[y1],retain_graph = True)[0]
dy1_dw1 = torch.autograd.grad(y1,[w1],retain_graph = True)[0]
dy2_dw1 = torch.autograd.grad(y2,[w1],retain_graph = True)[0]
dy2_dy1*dy1_dw1
tensor(2.)
dy2_dw1
tensor(2.)
十一、神经网络
1、梯度下降中的两个问题
在梯度下降最开始时,会先随机设定初始权重w(0),对应的纵坐标L(w(0))就是初始的损失函数值,坐标点(w(0),L(w(0)))就是梯度下降的起始点。接着从起始点开始,让自变量w向损失函数L(w)减小最快的方向移动。起始点是一个“盲人”,没有上帝视角。它不知道全局,每次走都得用“拐杖”——梯度向量确定方向。每次走都只敢走一小段距离,每走一下都要重新确认方向。只有这样走很多步之后,才能够到达或接近损失函数的最小值。 每步的方向就是当前坐标点对应的梯度向量的反方向,每步的距离就是步长 * 当前坐标点所对应的梯度向量的大小(即梯度向量的模长)。
(1)怎么找出梯度的大小和方向?
梯度向量中的具体元素就是各个自变量的偏导数,这些偏导数的具体值必须依赖于当前所在的坐标点(w,loss)的值进行计算。
让坐标点动起来(进行一次迭代):本质就是在原来坐标的基础上分别减去相应的值。
偏导数的大小影响整体梯度向量的大小,偏导数前的“-”号影响整体梯度向量的方向。
(2)怎么让坐标点按照梯度向量的反方向 移动与梯度向量大小相等的距离?
(3)
MLP(multi-layer perceptron)反向传播
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits import mplot3d
import numpy as np
w1 = np.arange(-10,10,0.05)
w2 = np.arange(-10,10,0.05)
w1,w2 = np.meshgrid(w1,w2)
lossfn = (2 - w1 - w2)**2 + (4 - 3*w1 - w2)**2
def plot_3D(elev=45,azim=60,X=w1,y=w2):
fig,ax = plt.subplots(1,1,constrained_layout=True,figsize=(8,8))
ax = plt.subplot(projection="3d")
ax.plot_surface(w1,w2,lossfn,cmap="rainbow",alpha=0.7)
ax.view_init(elev=elev,azim=azim)
ax.set_xlabel("w1",fontsize=20)
ax.set_ylabel("w2",fontsize=20)
ax.set_zlabel("lossfn",fontsize=20)
plt.show()
from ipywidgets import interact,fixed
interact(plot_3D,elev=[0,15,30],azip=(-180,180),X=fixed(w1),y=fixed(w2))
plt.show()
interactive(children=(Dropdown(description='elev', options=(0, 15, 30), value=0), IntSlider(value=60, descript…
2、反向传播
pytorch可以帮我们自动计算梯度,我们只需要提取梯度向量的值就行。
学习率一般是0.02~0.5
3、2维函数优化实战
Himmelblau function: f(x,y) = (x2 + y - 11)2 + (x + y2 - 7)2
import numpy as np
import matplotlib.pyplot as plt
def himmelblau(x):
return(x[0] **2 +x[1]-11)**2 + (x[0]+x[1]**2 - 7)**2
x = np.arange(-6,6,0.1)
y = np.arange(-6,6,0.1)
print('x,y range:',x.shape,y.shape)
X,Y = np.meshgrid(x,y)
print('X,Y maps:',x.shape,y.shape)
Z = himmelblau([X,Y])
fig = plt.figure('himmelblau')
ax = fig.gca(projection = '3d')
ax.plot_surface(X,Y,Z)
ax.view_init(60,-30)
ax.set_xlabel('x')
ax.set_ylabel('y')
plt.show()
x,y range: (120,) (120,) X,Y maps: (120,) (120,)
x = torch.tensor([0.,0.],requires_grad = True)
optimizer = torch.optim.Adam([x],lr = 1e-3)
for step in range(20000):
pred = himmelblau(x)
optimizer.zero_grad()
pred.backward()
optimizer.step()
if step % 2000 == 0:
print('step{}:x = {},f(x) = {}'
.format(step,x.tolist(),pred.item()))
step0:x = [0.0009999999310821295, 0.0009999999310821295],f(x) = 170.0
step2000:x = [2.3331806659698486, 1.9540694952011108],f(x) = 13.730916023254395
step4000:x = [2.9820079803466797, 2.0270984172821045],f(x) = 0.014858869835734367
step6000:x = [2.999983549118042, 2.0000221729278564],f(x) = 1.1074007488787174e-08
step8000:x = [2.9999938011169434, 2.0000083446502686],f(x) = 1.5572823031106964e-09
step10000:x = [2.999997854232788, 2.000002861022949],f(x) = 1.8189894035458565e-10
step12000:x = [2.9999992847442627, 2.0000009536743164],f(x) = 1.6370904631912708e-11
step14000:x = [2.999999761581421, 2.000000238418579],f(x) = 1.8189894035458565e-12
step16000:x = [3.0, 2.0],f(x) = 0.0
step18000:x = [3.0, 2.0],f(x) = 0.0
loss != accuracy
logits = torch.rand(4,10)
F.softmax(logits,dim=1).argmax(dim=1)
tensor([1, 9, 5, 1])
logits.argmax(dim=1)
tensor([1, 9, 5, 1])
correct = torch.eq(logits.argmax(dim=1),torch.tensor([1,9,2,3]))
correct
tensor([ True, True, False, False])
correct.sum().float().item()/4
0.5
when to test?
test once per epoch test once per several batch
visdom可视化
pip install visdom
pip uninstall visdom
python -m visdom.server #开启web服务器,开启visdon
·一条曲线
from visdom import Visdom
viz = Visdom()
viz.line([0.],[0.],win = 'train_loss',opts = dict(title='train loss'))#创建直线:给初始点,窗口,命名
viz.line([loss.item()],[global_step],win='train_loss',update='append')#(图像/numpy,x坐标,窗口,添加在当前的直线后面)
·多条曲线
from visdom import Visdom
viz = Visdom()
viz.line([[y1,y2]],[x],win='标识符',opts(标题,[y1_label,y2_label]))
viz.line([[0.0,0.0]],[0.],win='test',opts=dict(title='test loss&acc.',
legend=['loss','acc.']))
viz.line([[test_loss,correct/len(test_loader.dataset)]],[global_step],win='test',update='append')
·visual x
from visdom import Visdom
viz = Visdom()
viz.images(data.view(-1,1,28,28),win='x')
viz.text(str(pred.detach().cpu().numpy()),win='pred',opts=dict(title='pred'))
过拟合与欠拟合
|