Think World

从个人成长角度来说,从经历中学点什么总是重要的

0%

梯度

基本概念

梯度是方向导数最大的地方

利用SGD深度学习的一般步骤

影响优化器的因素

每个都是一个研究方向,这里只是简单列出,只有有机会会分专题详细学习

梯度问题

局部最小值
鞍点

权重初始值

不仅仅课程中所提到的问题:由于之后网络层次比较深刻,所以会出现梯度消失或者梯度爆炸的问题

常见的初始化方法:[还没有彻底的理解,先学习一个框架]

https://blog.csdn.net/u012328159/article/details/80025785?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param

https://zhuanlan.zhihu.com/p/62850258

learning rate

逐步衰减;过大,可能会出现震荡,不会达到局部最优解;过小,优化的速度会很慢

momentum

动量:用来逃出局部最优解

激活函数与loss的梯度计算

注意!!!:梯度是向量,这是我之前一致都不大注意的点,将它与导数混淆

激活函数的概念

灵感来自 青蛙的神经元的结构–一个阈值函数:神经元并不是各个输入的加权求和而是只有大于某个阈值之后才会输出,输出值是固定的值

早期的激活函数

之前自己总结有各种激活函数

课程里所提到的几个简单的激活函数,以及对应的导数

在pytorch相对应的函数

1
2
3
4
5
6
torch.sigmoid()

torch.tanh()

torch.nn.functional.relu()

常见的loss函数

mean-square-error

Cross-Entropy-loss

Softmax

相关导数推导

mse
softmax

pytorch中的自动求导

torch.autograd.grad()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import torch.nn.functional as F
a = torch.rand(3) # [batch, class_num]
a.requires_grad_()
print(a)
p = F.softmax(a,dim=0)#在第0维上,也就是 class_num上,定义损失函数
print(p)

#方法2:

print(torch.autograd.grad(p[0],[a],retain_graph= True)) #单个计算每个损失函数

print(torch.autograd.grad(p[1],[a],retain_graph= True))

print(torch.autograd.grad(p[2],[a],retain_graph= True))


'''
tensor([0.5666, 0.6252, 0.9636], requires_grad=True)
tensor([0.2819, 0.2989, 0.4192], grad_fn=<SoftmaxBackward>)
(tensor([ 0.2024, -0.0842, -0.1182]),)
(tensor([-0.0842, 0.2096, -0.1253]),)
(tensor([-0.1182, -0.1253, 0.2435]),)
'''

loss.backword()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import torch.nn.functional as F
a = torch.rand(3) # [batch, class_num]
a.requires_grad_()
print(a)
p = F.softmax(a,dim=0)#在第0维上,也就是 class_num上,定义损失函数
print(p)

##p.backward() 报错 ,因为此时p为向量,没有办法进行求导
# pytorch: grad can be implicitly created only for scalar outputs
##只能对单个y进行自动求导,
#所以单独列开,同时由于p[0]-p[2]都使用同一个计算图,
# 但是pytorch的计算图如果没有显式声明要保存,计算一次之后会作废,所以要置retain_graph=True
# retain_graph 有效次数是1次,即如果下次还需要用到计算图,还是需要置为True

p[0].backward(retain_graph =True)
p[1].backward(retain_graph =True)
p[2].backward(retain_graph =True)
print(a.grad)

'''
tensor([0.1281, 0.9067, 0.9391], requires_grad=True)
tensor([0.1842, 0.4013, 0.4145], grad_fn=<SoftmaxBackward>)
tensor([1.4901e-08, 4.4703e-08, 1.4901e-08])

'''

两种方法对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
torch.autograd.grad(

outputs: Union[torch.Tensor, Sequence[torch.Tensor]],

inputs: Union[torch.Tensor, Sequence[torch.Tensor]],

grad_outputs: Union[torch.Tensor, Sequence[torch.Tensor], None] = None,

retain_graph: Optional[bool] = None, # True--------the function will only return a list of gradients w.r.t the specified inputs.
# False, will be accumulated into their .grad attribute.

create_graph: bool = False,

only_inputs: bool = True, # True--------the function will only return a list of gradients w.r.t the specified inputs.
# False, will be accumulated into their .grad attribute.


allow_unused: bool = False) → Tuple[torch.Tensor, ...]


1
2
3
4
5
6
7
8
9
10
11
12
13
torch.autograd.backward(
tensors: Union[torch.Tensor, Sequence[torch.Tensor]],

grad_tensors: Union[torch.Tensor, Sequence[torch.Tensor], None] = None,

retain_graph: Optional[bool] = None,

create_graph: bool = False,

grad_variables: Union[torch.Tensor, Sequence[torch.Tensor],None] = None) → None


#该函数是将所有的梯度计算都累加到需要计算梯度的变量的grad属性中

官方文档建议使用第一种,因为第二种存在内存泄漏问题(也不是很清楚

遇到的bug和解决问题

代码背景

1
2
3
4
5
6
import torch.nn.functional as F
a = torch.rand(3) # [batch, class_num]
a.requires_grad_()
print(a)
p = F.softmax(a,dim=0)#在第0维上,也就是 class_num上,定义损失函数
print(p)

RuntimeError: Trying to backward through the graph a second time, but the buffers have already been freed. Specify retain_graph=True when calling backward the first time.

修改方案: retain_graph

pytorch: grad can be implicitly created only for scalar outputs

数学运算

1. element-cal

1
2
3
4
5
6
7
8
9
10
11
12
#汇总
torch.add ; +
torch.sub; -
torch.div ; /
torch.mul; *
a.pow(n) ; a**n
a.sqrt()
a.rsqrt() #平方根的倒数
a.exp()
a.log()
a.log2()
a.log10()

利用重载后的运算符

2.矩阵的乘除

1
2
3
torch.mm()#只适用于2D
@
torch.matmul()

2.1 2D矩阵的乘除

2.2 多维矩阵的乘除

实际就是支持多个矩阵对并行相乘[只计算最低的两维数据]

broadcasting机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#在计算过程中,可以使用broadcasting对矩阵进行扩展,
#可以使用matmul的标准:1. 低维(1,2)数据要满足矩阵乘法的行/列数的要求,
#高维数据要完全匹配或者经过broadcasting机制可以匹配
#一维数据,求点积

a = torch.tensor([2])
b= torch.tensor([3])
print(torch.matmul(a,b))


print(torch.matmul(a,b).shape) #计算得到标量

#只计算低维数据,高维数据完全匹配
a = torch.rand(4,3,28,64)
b = torch.rand(4,3,64,32)
c = torch.matmul(a,b)

print(c.shape)
#broadcasting 机制
b = torch.rand(64,32)

print(torch.matmul(a,b).shape)

b = torch.rand(4,1,64,32)


print(torch.matmul(a,b).shape)


a = torch.rand(28,64)
b = torch.rand(1,3,64,32)

print(torch.matmul(a,b).shape)


'''
output:

tensor(6)

torch.Size([])

torch.Size([4, 3, 28, 32])

torch.Size([4, 3, 28, 32])

torch.Size([4, 3, 28, 32])

torch.Size([1, 3, 28, 32])

'''

2.3其他计算

1
2
3
4
5
6
7
8
9
10
11
12
a.floor()

a.ceil()

a.round()#四舍五入

a.trunc()#取整数部分

a.frac()#取小数部分

a.clamp(min,max) #如果a[i]<min ,a[i] =min ; 如果a[i]>max ,a[i]=max

函数相关实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14

a = torch.rand(2,3)*15
print(a)
print(a.clamp(5,10))

'''

tensor([[ 0.1522, 8.6243, 10.4471],
[ 2.0610, 8.4947, 10.7278]])


tensor([[ 5.0000, 8.6243, 10.0000],
[ 5.0000, 8.4947, 10.0000]])
'''

统计属性

1
2
##相关函数计算

附录:

1。在某维度上argmax的理解—感觉自己对抽象几何不是特别理解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
a = torch.randn(4,10)a
print(a)
print(a.argmax())
print(a.argmax(dim=0))
print(a.argmax(dim=1))
'''
tensor([[ 7.0244e-01, -1.8047e-01, -1.3860e+00, -2.7422e-02, 1.0189e-01,
-4.1896e-01, 2.3863e+00, -2.3053e-01, -1.7369e-02, 1.8899e-01],
[ 1.0445e-01, -5.9622e-01, -8.3666e-01, -7.0087e-01, -2.1692e-01,
1.2431e-01, -2.3204e-01, -5.3630e-01, -1.1655e+00, 4.6021e-01],
[-1.1210e+00, -4.4479e-01, 1.0892e+00, -1.1631e+00, 7.8703e-01,
-3.9018e-01, -6.1701e-01, 8.2018e-01, 1.2167e-01, -1.6905e+00],
[-1.4846e+00, 9.5270e-01, 2.2757e-01, 1.5931e+00, 2.9075e-01,
1.3509e+00, 1.5353e+00, -1.1048e+00, 7.0249e-04, -1.1067e-01]])
tensor(6)
tensor([0, 3, 2, 3, 2, 3, 0, 2, 2, 1])
tensor([6, 9, 2, 3])
'''

2.在某维度上范式的计算的理解

1
2
3
4
5
6
7
8
9
10
11
12
a = torch.FloatTensor([1.,2.,3.,4.,5.,6.,7.,8.,9.,10.])
b = a.view(2,5)
print(b)
print(b.norm(1,dim=0))
print(b.norm(1,dim=1))
'''
tensor([[ 1., 2., 3., 4., 5.],
[ 6., 7., 8., 9., 10.]])
tensor([ 7., 9., 11., 13., 15.])
tensor([15., 40.])

'''

高阶操作

where 语句

代替for循环,可以利用GPU进行加速计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
cond = torch.tensor([[0.6769,0.7271],[0.8884,0.4163]])

a = torch.tensor([[0.,0.],[0.,0.]])

b = torch.tensor([[1.,1.],[1.,1.]])

print(torch.where(cond>0.5,a,b))


##同上面等效,可以体会一下语义
c =torch.randn(2,2)
for i in range(2):
for j in range(2):
if(cond[i][j]>0.5):
c[i][j] = a[i][j]
else:
c[i][j] = b[i][j]
print(c)

'''
output:

tensor([[0., 0.],
[0., 1.]])


tensor([[0., 0.],
[0., 1.]])

'''

gather 语句

提出的背景

可以自行体会含义

应用场景

合并与分割

1. cat

2. stack

cat v.s. stack

3. split

by sub-len

1
2
3
4
torch.chunk(sub_size ,dim =dd)	#拆分成若干个size都为sub_size的tensor

torch.chunk([sub.size1,sub_size2.....],dim=d) #拆分成各分布为size1,size2...的tensor

4. chunk

by sub-num

1
torch.chunk(sub_num ,dim =dd)

汇总代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
a = torch.randn(32,8)
a1 = torch.randn(32,8)
a2 = torch.randn(32,8)
b = torch.randn(32,8)
b2= torch.randn(32,8)
b3 = torch.randn(32,8)


c = torch.stack([a,b],dim=0)
d = torch.cat([a,b],dim=0)
d = torch.cat([a1,d],dim=0)
d = torch.cat([a2,d],dim=0)
d = torch.cat([b2,d],dim=0)
d = torch.cat([b3,d],dim=0)

print(d.shape)

n,m = d.split([2*32,4*32],dim=0) #指定每个sub-tensor的size,使用list表示,所有size的和必须等于拆分前的tensor的size

print(n.shape)
print(m.shape)


nn,mm = d.split(3*32,dim=0) #如果所有长度都固定,就用一个数来表示每个sub-tensor的长度,sub-tensor的个数可以自动计算得到
# nn,mm = d.split(3,dim=0) #报错,如果每个sub-tensor第一维的size=3,会返回64个sub-tensor
print(nn.shape)
print(mm.shape)


nnn ,mmm = d.chunk(2,dim=0)#拆分成两个tensor,每个tensor的长度相同为3*32
print(nnn.shape)
print(mmm.shape)

1.Broadcasting的基本准则

例子

2.Broadcasting引入的背景

1. 实际计算需求:可以允许不同shape(但又满足某种标准)的tensor可以进行计算
 2. 同时这种原则满足数学上的运算,又无需手动操作,又不会消耗很多的内存(对比repeat)

维度变换

数据没有被改变,改变的只有数据的理解方式

1. view / reshape

1.view reshape ,两个方法的用法完全相同
要求转换前后tensor的numel()相同即可,prod(a.size()) ==prod(b.size())

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

a = torch.randn(4,1,28,28)
print(a.shape)
#把每张照片打成一维数据,这个常做为-卷积网络的输入
b = a.view(4,28*28)
print(b.shape)
#把只是关注照片中每行的特点
c = a.view(4*1*28,28)
print(c.shape)

#不关心像素点是来自哪里,只是关注图片中不同像素点的不同
d = a.view(4*1,28,28)
print(d.shape)

#数据的存储/维度顺序非常重要,但是这里view 和reshape函数在转换时把这一信息丢掉了,无额外记录之前的形状,无法恢复

'''
Output:

torch.Size([4, 1, 28, 28])
torch.Size([4, 784])
torch.Size([112, 28])
torch.Size([4, 28, 28])


'''

2.squeeze v.s. unsqueeze-维度

2.1 unsqueeze()

1
2
3
4
5
6
7
8
9
#unsqueeze( input , dim ) ->Tensor

#####参数说明:
#input (Tensor) – the input tensor.
# dim (int) – the index at which to insert the singleton dimension,dim的取值范围[- input.dim() -1 , input.dim()+1 ]

#如果dim为负数,则 dim = dim + input.dim()+1 将其转换成正数
#如果dim为整数,则 新增的维度添加到 dim的前面
#维度上的元素个数只有一个,所以数据规模没有改变,改变的只是数据的含义

图示

可以看出来维度变换之后,数据的理解方式不同,要好好体会

数据处理的实例应用

1
2
3
4
5
6
7
8
9
10
11
12
13
b = torch.rand(32)
f = torch.rand(4,32,14,14)
b = b.unsqueeze(-1).unsqueeze(-1).unsqueeze(0)
print(b.shape)
#方便之后 f+b的计算。b 即bias,相当于给每个channel上的所有像素增加一个偏置

'''
output : torch.Size([1, 32, 1, 1])

'''
##常用的: 就是如果向后面插就使用 unsqueeze(-1)多次插入,就多次写
#向开头插入就,直接调用 unsqueeze(0)

2.2 squeeze

1
2
3
4
5
6
7
8
9
10
11
#torch.squeeze(input , dim =None ,Out = None) ->tensor
#当不传参数时,会将input所有元素只有一个的维度给去掉
#传参数dim,在指定维度上且维度只有一个元素时,将挤压掉该维度

#b.shape = torch.Size([1,32,1,1])
print(b.squeeze(-1).shape)
print(b.squeeze(1).shape) #无效
'''
torch.Size([1, 32, 1])
torch.Size([1, 32, 1, 1])
'''

3.Expand /repeat-行

3.1 expand

参数是广播的目标tensor的shape

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

a = torch.rand(4,32,14,14)

b = torch.rand(32)

b = b.unsqueeze(-1).unsqueeze(-1).unsqueeze(0)

b1 = b.expand(4,32,14,14)#之后b2即可以与a进行运算

print(b1.shape)
# b2 = b.expand(4,33,14,14) 会报错 The expanded size of the tensor (33) must match the existing size (32) at non-singleton dimension 1

b2 = b.expand(-1,-1,2,3) #-1表示不想对该维度进行修改

print(b2.shape)

print(b.shape)

3.2 repeat--不建议使用

进行repeat之后,可能会开辟新的空间去保存repeat的结果,会降低效率

参数传递是每个维度上要重复的次数,需要自己计算

4.转置操作 .t(只能适用于2D操作)

5.Transpose()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#转换效果图

a = torch.randn(2,3,2)
print(a)
b = torch.transpose(a,1,2)

c = torch.transpose(a,1,2).contiguous().view(2,2*3).view(2,3,2)

d = torch.transpose(a,1,2).contiguous().view(2,2*3).view(2,2,3).transpose(1,2)


print(b)
print(c)
print(d)

'''
tensor([[[-0.2507, -0.4298],
[-2.8421, 0.9166],
[ 3.2584, 1.1366]],

[[ 1.8887, -0.5606],
[ 1.3798, -0.5037],
[ 0.9862, 0.8550]]])


tensor([[[-0.2507, -2.8421, 3.2584],
[-0.4298, 0.9166, 1.1366]],

[[ 1.8887, 1.3798, 0.9862],
[-0.5606, -0.5037, 0.8550]]])


tensor([[[-0.2507, -2.8421],
[ 3.2584, -0.4298],
[ 0.9166, 1.1366]],

[[ 1.8887, 1.3798],
[ 0.9862, -0.5606],
[-0.5037, 0.8550]]])


tensor([[[-0.2507, -0.4298],
[-2.8421, 0.9166],
[ 3.2584, 1.1366]],

[[ 1.8887, -0.5606],
[ 1.3798, -0.5037],
[ 0.9862, 0.8550]]])
'''

6.permute

permute也会打乱内存的顺序,需要调用coutigious函数

permute底层是调用多次transpose()实现的

1
2
3
#上面遮挡住的命令是
b.permute(0,2,3,1).shape
#out : torch.Size([4,28,32,3])

1.pytorch风格的索引-维度选择

跟据Tensor的shape,从前往后索引,依次在每个维度上进行索引

1
2
3
4
5
6
7
8
9
10
11
12
import torch

a = torch.rand([4,3,28,28])
print( a[0].shape)#和C++等高级问题类似,a[0]表示选择第一章图片
print(a[0,0].shape)#选择第一章图片的第一个通道
print(a[0,0,2,4])#选择某个像素点,

'''
torch.Size([3, 28, 28])
torch.Size([28, 28])
tensor(0.1076) #是维度为0的元素
'''

2.python风格的索引-特定维度上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
'''
基本结构:
eg : a[0,0,0:28:2,0]
#0:28:2--在某个维度上,start: end[:offset] 其中start:end:offset 表示从start开始,到end结束,其中不包括end,每次步长为offset;当省略offset时,表示offset=1
# 0:28其实等价与 0:28:1这种结构

-1:表示倒数第1个元素,-2表示倒数第2个元素
'''

import torch

# 譬如:4张图片,每张三个通道,每个通道28行28列的像素
a = torch.rand(4, 3, 28, 28)

# 在第一个维度上取后0和1,等同于取第一、第二张图片
print(a[:2].shape)

# 在第一个维度上取0和1,在第二个维度上取0,
# 等同于取第一、第二张图片中的第一个通道
print(a[:2, :1, :, :].shape)

# 在第一个维度上取0和1,在第二个维度上取1,2,
# 等同于取第一、第二张图片中的第二个通道与第三个通道
print(a[:2, 1:, :, :].shape)

# 在第一个维度上取0和1,在第二个维度上取1,2,
# 等同于取第一、第二张图片中的第二个通道与第三个通道
print(a[:2, -2:, :, :].shape)

# 使用step隔行采样
# 在第一、第二维度取所有元素,在第三、第四维度步长为2采样
# 等同于所有图片所有通道的行列每个一行或者一列采样
# 注意:下面的代码不包括28
print(a[:, :, 0:28:2, 0:28:2].shape)
print(a[:, :, ::2, ::2].shape) # 等同于上面语句

3. 选择特定的元素

3.1 index_select

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
'''
torch.index_select(input, dim, index, out=None) → Tensor

input (Tensor) – the input tensor.
dim (int) – the dimension in which we index
index (LongTensor) – the 1-D tensor containing the indices to index;是list转换成的一维tensor
out (Tensor, optional) – the output tensor.
'''

a = torch.rand(4,3,28,28)
print(a.shape)
print((a.index_select(0,torch.tensor([0,2]))).shape)
#选取第一个维度上的 索引为0,2的tensor
#第二个参数是将list [0,2]转换成 tensor
print((a.index_select(2,torch.arange(8))).shape)

#选择第二维度上的前8个元素

'''
torch.Size([4, 3, 28, 28])
torch.Size([2, 3, 28, 28])
torch.Size([4, 3, 8, 28])
'''

索引效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
a = torch.rand(3,3)
print(a)
print((a.index_select(0,torch.tensor([0,2]))))
print((a.index_select(1,torch.arange(2))))

'''
tensor([[0.9071, 0.5350, 0.4018],
[0.9565, 0.9739, 0.7234],
[0.1984, 0.3562, 0.8078]])

tensor([[0.9071, 0.5350, 0.4018],
[0.1984, 0.3562, 0.8078]])

tensor([[0.9071, 0.5350],
[0.9565, 0.9739],
[0.1984, 0.3562]])
'''

3.2 masked_select()

方式一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
'''

函数说明
torch.masked_select( input, mask ,out = None ) -> 张量


根据掩码张量mask中的二元值(0,1),取输入张量中的指定项( mask为一个 ByteTensor),将取值返回到一个新的1D张量---是打平的张量; 张量 mask须跟input张量有相同数量的元素数目,但形状或维度不需要相同。
注意: 返回的张量不与原始张量共享内存空间。
'''
a = torch.randn(3,4)
print(a)
mask2 = a.ge(0.3)
mask3 = a.le(0.7)
print(mask2)
print(a[mask2])
print(a[mask3])
print(torch.masked_select(a,mask2))

'''
output:
tensor([[-0.7769, -0.0803, -0.4235, -0.3562],
[-0.4744, 1.2078, 0.6371, -0.6981],
[-1.1653, -0.3432, -2.3189, 0.1708]])

tensor([[ True, False, True, True],
[False, False, False, False],
[ True, False, False, False]])

tensor([1.2078, 0.6371])

tensor([-0.7769, -0.0803, -0.4235, -0.3562, -0.4744, 0.6371, -0.6981, -1.1653,
-0.3432, -2.3189, 0.1708])

tensor([1.2078, 0.6371])

'''
方式二 不使用a.ge() \ a.le()方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

a = torch.randn(3,4)
print(a)
mask1 = torch.ByteTensor((a>0.5).byte()) 
print(mask1)

print(a[mask1])

'''
输出:
tensor([[-1.8973, -0.2158, 1.0196, -0.2119],
[-0.2365, 0.4743, -1.2473, -0.6554],
[ 0.3040, -0.0906, -0.8517, -0.2679]])
tensor([[0, 0, 1, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]], dtype=torch.uint8)
tensor([1.0196])


提醒一下:不是特别建议使用这种方式
UserWarning: indexing with dtype torch.uint8 is now deprecated, please use a dtype torch.bool instead.

感觉自己对python和pytorch基本类型的转换有些模糊 ---torch.bool 与 torch.uint8
'''

3.2 torch.take()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
'''
使用打平的index进行索引
torch.take(input, index) ->Tensor

index(Long Tensor) 把input Tensor看作一维Tensor对每个元素的索引
输出:一个一维Tensor
'''
a = torch.randn(3,4)
print(a)
b = torch.take(a, torch.tensor([1,5,7]))
print(b)

'''
输出:
tensor([[-0.3353, 1.2220, 0.7055, 0.4678],
[-0.4759, 0.5173, 1.1912, -0.8545],
[-1.2037, 0.5052, 0.0388, -0.3160]])

tensor([ 1.2220, 0.5173, -0.8545])

'''

手写数字识别

MNIST 数据集

由不同风格的手写数字组成(0-9)

每个数字都有7000张,每张图片都是28*28的灰度图片

训练时:将训练集和测试集分为60k Vs 10k

1. No deeping learning

X:[1….28*28]

每个点都是0-1,表示该像素点的灰度值

关键点:参数的维度定义,以及每层转换的含义

2.代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#-*-encodng: utf-8-*-
'''
@File: minist.py.py
@Contact: 2257925767@qq.com
@Author:wangyu
@Version:

@Desciption:
手写数字识别的核心代码

还存在一些问题没有解决---自己得到的数据比老师的代码迭代的次数要少很多
env:
pytorch 1.3.1
@DateTime: 2020/8/22下午5:04
'''

import torch
from torch import nn
from torch.nn import functional as F #常见的激活函数

from torch import optim

import torchvision

from matplotlib import pyplot as plt

from util import plot_image, plot_curve, one_hot

# step1 . load dataset,采用向量并行

batch_size = 512

train_loader = torch.utils.data.DataLoader(
torchvision.datasets.MNIST("mnist_data",train=True,download=True,
transform= torchvision.transforms.Compose([torchvision.transforms.ToTensor(),#将numby格式数据转成pytorch
torchvision.transforms.Normalize(
(0.1307,),(0.3081,)), #对像素点的灰度值进行正则化,会提高优化效率
])),batch_size = batch_size,shuffle = True)#随机打散

test_loader = torch.utils.data.DataLoader(
torchvision.datasets.MNIST("mnist_data/",train=False,download=True,
transform= torchvision.transforms.Compose([torchvision.transforms.ToTensor(),#将numby格式数据转成pytorch
torchvision.transforms.Normalize(
(0.1307,),(0.3081,)), #对像素点的灰度值进行正则化,会提高优化效率
])),batch_size = batch_size,shuffle = False)#随机打散


x,y = next(iter(train_loader))
print(x.shape,y.shape,x.min(),x.max())
plot_image(x,y,'image_test') #检查数据集

class Net(nn.Module):

def __init__(self):#初始化函数:搭建网络结构
super(Net,self).__init__()

#wx+b
self.fc1 = nn.Linear(28*28,256)
self.fc2 = nn.Linear(256,64)
self.fc3 = nn.Linear(64,10)#28*28 => 256 =>64这个是随机确定的,最后一层的输出是由分类的种类数决定的

def forward(self,x):#网络的计算过程
# x :[batch_size,1,28,28] 1:表示只有一个通道

#h1= relu(xw1+b1)
x = F.relu(self.fc1(x))
#h1=relu(h1w2+b2)
x=F.relu(self.fc2(x))
#h3=h2w3+b3 --这里没有使用激活函数,只是简单输出
x=self.fc3(x)

return x

net = Net()#实例化一个net

#定义优化器
optimizer = optim.SGD(net.parameters(),lr=0.01,momentum=0.9)

train_loss=[]

for epoch in range(3):
for batch_idx , (x,y) in enumerate(train_loader): #迭代一次数据集

#1.调整数据的尺寸,构建网络,通过网络计算预测值

#x: [b,1,28,28], y:[512]
# [b,1,28,28] => [b,784]
x =x.view(x.size(0),28*28) #size[0]表示batch_size
#=>[b,10]
out = net(x) #经过网络计算出来的值
y_onehot = one_hot(y)

#2.定义梯度
#loss = mse_loss(out,y_onehot) 欧式距离
loss = F.mse_loss(out,y_onehot)
#3.梯度清0
optimizer.zero_grad()

#4.梯度计算过程
loss.backward()

#5.参数更新
#w'= w-lr*grad
optimizer.step()

train_loss.append(loss.item())#loss: tensor => numpy

if batch_idx % 10 == 0:
print(epoch,batch_idx,loss.item())

plot_curve(train_loss)

#get optimal[w1,b2,w2,b2,w3,b3]

total_correct = 0
for x,y in test_loader:
x = x.view(x.size(0),28*28)
out = net(x)
#out = net(x)
pred = out.argmax(dim=1)

correct = pred.eq(y).sum().float().item()
total_correct+= correct

total_num = len(test_loader.dataset)
acc=total_correct/total_num
print('test acc:',acc)

x,y = next(iter(test_loader))
out = net(x.view(x.size(0),28*28))
pred = out.argmax(dim=1)
plot_image(x,pred,'test')



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#-*-encodng: utf-8-*-
'''
@File: util.py.py
@Contact: 2257925767@qq.com
@Author:wangyu
@Version:
手写数字识别的工具文件
@Desciption:
env:

@DateTime: 2020/8/22下午5:04
'''

import torch
from matplotlib import pyplot as plt


def plot_curve(data):#计算训练曲线
fig = plt.figure()
plt.plot(range(len(data)), data, color='blue')
plt.legend(['value'], loc='upper right')
plt.xlabel('step')
plt.ylabel('value')
plt.show()



def plot_image(img, label, name):#展现出识别结果

fig = plt.figure()
for i in range(6):
plt.subplot(2, 3, i + 1)
plt.tight_layout()
plt.imshow(img[i][0]*0.3081+0.1307, cmap='gray', interpolation='none')
plt.title("{}: {}".format(name, label[i].item()))
plt.xticks([])
plt.yticks([])
plt.show()


def one_hot(label, depth=10):#完成one-hot编码
out = torch.zeros(label.size(0), depth)
idx = torch.LongTensor(label).view(-1, 1)
out.scatter_(dim=1, index=idx, value=1)
return out

简单的回归问题

1.从简单到复杂

==梯度下降算法==:梯度是深度学习的核心

1.简单的小例子

2.问题的迭代

求解$y = wx+b$ 二元一次方程$w, b$的值

  i.中学阶段: 求解二元一次方程的方法-->消元法(利用Closed Form Solution精确求得w,b的解)

​ ii.引入噪音(noise:),模拟现实情况,我们的目标并不是为了得到一个精确解,而是得到一个从经验上精度可行的近似解即可,

解决方法:需要更多的样本点+之后采用梯度下降算法求解

​ iii.首先构造一个函数(均方差),因为梯度下降算法是求解极值的算法


$$
loss = (y-wx-b)^2
$$
$loss$方程值最小所对应的$w,b$ 可以近似认为是二元一次方程的解

​ 注:在实际问题中,首先根据样本分布的情况,选择它可能对应的方程(二元一次,二元二次….)

​ V:优化过程(Convex Optimization-凸优化问题)

​ 针对于一个样本来讲
$$
对于loss函数,变量w,b;按照梯度下降的算法求loss的极值\
找到loss最小时,对应的w,b\
通过梯度迭代更新\
initial : w=0,b=0

\
b = b+ learningRate* \partial w\
w = w+ learningRate*\partial b

\
\partial w = 2(y-wx-b)*(-x)
\
\partial b= 2(y-wx-b)(-1)
$$

之后编程实现的是在N个样本上的问题

2.问题类型

3. 二元一次方程 编程实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import numpy as np

def computer_error_for_line_given_points(b,w,points): ##计算错误率
totalError = 0
for i in range(0,len(points)):
x=points[i,0]
y=points[i,1]
totalError += (y-(w*x+b))**2
return totalError/(float)(len(points))

def step_gradient(b_current,w_current,points,learningRate):##在所有的节点上进行,一次梯度下降,更新参数
b_gradient=0
w_gradient=0
N= float(len(points))
for i in range(0,len(points)):
x=points[i,0]
y=points[i,1]
b_gradient += -(2/N) *(y-((w_current*x)+b_current))
w_gradient += -(2/N)*x*(y-((w_current*x)+b_current))
new_b = b_current -(learningRate*b_gradient)
new_w = w_current -(learningRate*w_gradient)

return[new_b,new_w]

def gradient_descent_runner(points,starting_b,starting_w,learning_rate,num_iterator):

b= starting_b
w= starting_w

for i in range(num_iterator):
b,w = step_gradient(b,w,np.array(points),learning_rate)#这里应该是没有选择最小的loss只是把最后迭代的结果返回
return [b,w]

#这里没有数据
def run():
points = np.genfromtxt("/home/doriswang/workplace/coding/pytorch_learning/venv/include/2.1/data.csv",delimiter=",")
# print(points)
learning_rate =0.0001
initial_b =0
initial_w =0
num_iterations = 1000
print("Starting gradient descent at b={0} , w={1} ,error ={2}".format(initial_b,initial_w,computer_error_for_line_given_points(initial_b,initial_w,points)))
[b,w]=gradient_descent_runner(points,initial_b,initial_w,learning_rate,num_iterations)
print("After {0} iterations b={1},w={2},error={3}".format(num_iterations,b,w,computer_error_for_line_given_points(b,w,points)))

if __name__ == '__main__':
run()

初试深度学习

1.深度学习框架

==pytorch && tensorflow的本质区别在于动态图优先还是静态图优先==

1.动态优先图–pytorch

2.静态图的方式–tensorflow

1.首先先建立一个计算图(框架)

2.向计算图传递参数来运行计算图

在计算图运行过程中,我们不能干预,调试或者动态改变比较麻烦

综合评价

2.pytorch的生态

3.pytorch的优势

1.使用GPU进行加速

2.自动求导

3.常用网络层

开发环境

1 基本数据类型

1.1 python中数据类型与torch中数据类型的对比

《下图中常用的数据类型使用红色方框表示出来》

1.2 pytorch中String的表示方法

1.3 pytorch中CPU和GPU数据类型的区别

如果在GPU上面,则需要将其数据类型转换:
方法:``data=data.cuda()` ,调用此函数会返回一个GPU上的一个应用

1.4 torch中数据类型的判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import torch
# torch中的数据类型
a = torch.rand(2,3)#随机初始化一个两行三列的数据
print(a.type()) #查看数据类型
print(type(a))
print(isinstance(a,torch.FloatTensor)) #数据类型合法性检验
print(isinstance(a,torch.IntTensor))

'''
output:
torch.FloatTensor
<class 'torch.Tensor'>
True
False
'''

1.5 Tensor的形状

data.shape, data.size(),data.dim(),data.numel()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# dim =  size(data.shape) ,表示数据的深度/维度
# size=shape,表示数据的形状,记录每一维度的长度
# numel:表示tensor占用内存的长度
d4 = torch.FloatTensor(2,3)
print(d4.size())
print(d4.shape)
print(d4.dim())
print(d4.size(0)) #可以进行索引,某维度的长度,为了更好地与python进行交互,一般直接将size和shape转换成list
print(d4.size(1))

'''
output:

torch.Size([2, 3])
torch.Size([2, 3])
2
2
3
'''

1.6 Dim 0的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
a=torch.tensor(1.0)
print(a.shape) #1.0/tensor(1.)是0维的是标量,但是[1.]是1维长度为1的Tensor
#标量的应用:计算的loss函数都是标量
print(len(a.shape))
print(a.size())
print(torch.tensor(2))

'''
output:
torch.Size([]),这里表示size中的‘list'长度为0
0
torch.Size([])
tensor(2)

'''

标量的用途: 1.常见是在计算loss函数的结果是用标量表示的

1.7 Dim 1的数据

常用于 1. Bias: wx+b 中的 b 2. Linear Input 例如,手写数字识别中的28*28 可以看作长度为784的一维数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
##Dim 1/ rank 1
#其实在tensor 0.3 之前没有dim=0的Tensor,实际上维度为1,size=1的数即可表示标量,不过0.4之后把他们区分开了

a1=torch.tensor([1.1])#dim=1,size=1
a2=torch.tensor([1.1,2.2]) # dim=1,size=2

a3=torch.FloatTensor(1) #这里的参数指定的是Dim1的长度,随机初始化
print(a1.size())
print(a2.size())
print(a3.shape)

'''
torch.Size([1])
torch.Size([2])
torch.Size([1])
'''

1.8 Dim2的数据

常用场景: 1. Linear Input batch [ batch_size , features_num]

a=torch.tensor([4,784])
其中4是指数据图片的数目,而784是指每一张图片的特征维度
适用于普通的机器学习数据

1.9 Dim 3的数据

RNN Input Batch [batch_size, sentence_num, word_one_sentence]

例如,对于RNN神经网络进行语音识别与处理时[10,20,100]表示:每个单词包含100个特征,一句话一共有10个单词,而每次输20句话

1.10 Dim 4 的数据

CNN input Batch [batch_size, channel_num, height, weight ] 

通道数,图片的高度,图片的宽度

2.创建Tensor

1. Import from numpy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import numpy as np
a = np.array([2,3.3]) # 数据实际是double类型
print(torch.from_numpy(a))

a = np.ones([2,3])
print(torch.from_numpy(a))

'''
tensor([2.0000, 3.3000], dtype=torch.float64)

tensor([[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64)

'''

2. Import from list

关于参数传递的小问题

1
2
Tensor / FloatTensor /IntTensor 等既可以传递数值型,也可以传递shape相关的类型的数据
tensor只能传递数据list作为参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
a = torch.tensor([2.,3.2])
b = torch.tensor([[2.,2.3],[1.,2.34]])
print(a)
print(b)
a2 = torch.FloatTensor([2.,3.2])##不建议用该方法,tensor可以 代替
a3 = torch.FloatTensor(2,3,4) #建议传递参数为shape参数
print(a2)
print(a3)

'''
tensor([2.0000, 3.2000])

tensor([[2.0000, 2.3000],
[1.0000, 2.3400]])

tensor([2.0000, 3.2000])

tensor([[[ 2.0281e+15, 4.5682e-41, 2.5162e-26, 4.5682e-41],
[ 2.0260e+15, 4.5682e-41, 2.5162e-26, 4.5682e-41],
[ 2.0260e+15, 4.5682e-41, 2.5157e-26, 4.5682e-41]],

[[ 2.0284e+15, 4.5682e-41, 2.6360e-26, 4.5682e-41],
[ 3.6957e+15, 4.5682e-41, 2.6361e-26, 4.5682e-41],
[ 2.0284e+15, 4.5682e-41, -2.9008e+29, 3.0639e-41]]])
'''

3. 未初始化的数据

4.随机初始化

1
2
3
4
5
6
#相关函数
torch.rand(Tensor.shape) #生成一个[0,1)之间的随机数
torch.randlike(Tensor tt)

torch.randint( int low , int high , [d1,d2....])#均匀采样,只采整数值
torch.randint_like(Tensor tt ,int low, int high)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
a= torch.rand(3,3)
print(a)

# a1 = torch.rand()

b = torch.rand_like(a)#会生成与a尺寸大小相同的随机数的tensor
print(b)
c = torch.randint(1,99,[3,3])
print(c)
d = torch.randint_like(c,1,10) #使用randint一定要指出数据范围
print(d)

'''
tensor([[0.9843, 0.0908, 0.6296],
[0.5270, 0.4654, 0.3570],
[0.9419, 0.1018, 0.1724]])

tensor([[0.3775, 0.3013, 0.3237],
[0.6003, 0.1313, 0.1082],
[0.7574, 0.4202, 0.0610]])

tensor([[20, 93, 85],
[20, 81, 95],
[22, 10, 88]])


tensor([[8, 9, 8],
[2, 7, 4],
[1, 4, 9]])
'''

5.生成正态分布

1
2
3
4
5
6
7
8
9
10
#相关函数说明
tensor.normal(mean,std,*,generator=None,out = None) ->Tensor
#参数说明:该方法只能生成一维,需要进行形状变换

#1. mean, std可以都为tensor,此时可以为每个元素指定分布来源
#2. mean,std可以一个为float/tensor,此时最后生成的Tensor的尺寸mean/std(数据类型为tensor)决定,此时所有数据共享均值或者方差
#3. mean,std都为float时,要求指明参数,此时所有数据都来源一个分布

tensor,randn(*size , out = None, dtype = None,....)#参数列表没有列全
#从平均值为0,方差为1的正态分布中返回一个填充有随机数的张量(也称为标准正态分布
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
a1 = torch.randn(2,3)
#从平均值为0,方差为1的正态分布中返回一个填充有随机数的张量,形状由参数决定(也称为标准正态分布)

print(a1)

#关于torch.normal:比较灵活,掌握一种方式就行
#方式1:保证mean和std的尺寸相同
a = torch.normal( mean = torch.arange(1.,11.),std = torch.arange(1,0,-0.1))
print(a)
#方式2:
b = torch.normal(mean = torch.full([10],0),std = 0.5) #这种方式也是可以实现所有数据来自一个分布
print(b)
c = torch.normal(mean = 10, std = torch.arange(5,0,-0.5))
print(c)
#方式3
d = torch.normal(10,0.1,size=(2,3))
print(d)

'''
tensor([[-0.6377, 0.0554, -0.7616],
[ 0.1244, -0.1076, 0.2659]])

tensor([-1.5609, 0.1449, 2.7791, 3.4830, 4.6098, 5.9147, 7.2475, 8.0593,
9.3134, 10.0676])

tensor([ 0.8977, -0.6111, -0.2214, -0.0326, 0.4336, -0.2131, -0.0297, -0.0359,
0.0294, -0.3355])

tensor([11.8549, 12.7589, 13.7979, 12.9034, 8.1115, 15.1006, 13.7763, 12.9549,10.0838, 10.1662])

tensor([[10.0064, 9.8803, 9.9584],
[10.1267, 10.2988, 9.8402]])
'''

6.生成元素相同的tensor

7.生成等差数列的tensor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#相关函数说明
torch.arange( start ,end , offset )
#torch.range(start,end, offset) #闭区间
torch.linspace(start, end, steps=100)
'''
Return
start (float) – the starting value for the set of points

end (float) – the ending value for the set of points

steps (int) – number of points to sample between start and end. Default: 100.
'''

torch.logspace(torch.logspace(start, end, steps=100, base=10.0, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) → Tensor

'''
Returns a one-dimensional tensor of steps points logarithmically spaced with base base between base^{start} and base^{end}
'''

???==如何设置base==

8.其他生成函数(Ones, zeros,eye)

9.randperm(random.shuffle)

??