目录
一、前言
博主是做图像处理的医学成像方向的,所以会介绍一部分医学成像方面的知识,之后会介绍一些环境搭建和pytorch深度图像处理方面的知识。
二、MRI成像方向相关
pythorch使用的是动态图,二TensorFlow使用的静态图
MRI成像 技术参数MRI的加速参数与技术汇总 - 百度文库
加速因子加快磁共振成像的参数汇总--磁共振快速成像技术 - 知乎
三、相关技术文档
pytorch中文文档PyTorch 入门 - PyTorch官方教程中文版
pycharm常用快捷键Pycharm 常用快捷键 - 暮良文王 - 博客园,按住ctrll加鼠标左键转到函数定义处
python画图常用Python之Matplotlib库常用函数大全(含注释)_程序人韭菜花的博客-CSDN博客_matplotlib函数大全
,matplot这个链接特别好用Matplotlib — Visualization with Python
numpy教程链接NumPy:NumPy 安装 | 菜鸟教程
panda库文件操作库pandas库简单入门_K同学啊的博客-CSDN博客_pandas库
seaborn库也是一个画图库画特征和特征之间的Python的seaborn库_fenfenxhf的博客-CSDN博客_python seaborn
seaborn库第一推荐Python数据可视化—seaborn简介和实例_wenwut的博客-CSDN博客_seaborn
shift+enter光标不在最后也可以换行,Tab 自动补全Ctrl+z 撤销,按住Ctrl,鼠标点击包名,能进入包,看底层代码
软件版本安装对应,选中多行,按tab进行缩进,按tab+shift去除缩进
四、环境搭建相关
https://jingyan.baidu.com/article/bea41d43d5741fb4c51be6f7.html cuda是否成功
遇到不熟的函数可以去里面查Docs | Apache MXNet
框架搭建顺序就是Anconda---->pycharm------>cuda----->cudnn---->pytorch
cuda安装1.windows下cuda的安装 - wenglabs - 博客园 2.windows下CUDA的卸载以及安装_花花少年的博客-CSDN博客_cuda卸载
五、深度学习常用的函数
5.1numpy的使用
bunpy的创建只需要调用Numpy的arry函数numpy.arry(列表)
import numpy as np
a = np.array([1,2,3],order='F')
print (a)
import numpy as np
a = np.array([[1, 2], [3, 4]])
print (a)
在[]里面一个中括号就是一行,下面是创建复数,数据类型是type即可
import numpy as np
a = np.array([1, 2, 3], dtype = complex)
print (a)
很多时候可以声明 axis。axis=0,表示沿着第 0 轴进行操作,即对每一列进行操作;axis=1,表示沿着第1轴进行操作,即对每一行进行操作,注意在Matlab和numpy中列在前
a = np.arange(24)就是产生0到23的一个列表,ndarray.ndim 用于返回数组的维数,等于秩,
ndarray.ndim | 秩,即轴的数量或维度的数量 |
ndarray.shape | 数组的维度,对于矩阵,n 行 m 列 |
ndarray.size | 数组元素的总个数,相当于 .shape 中 n*m 的值 |
ndarray.dtype | ndarray 对象的元素类型 |
ndarray.itemsize | ndarray 对象中每个元素的大小,以字节为单位 |
ndarray.flags | ndarray 对象的内存信息 |
ndarray.real | ndarray元素的实部 |
ndarray.imag | ndarray 元素的虚部 |
ndarray.data | 包含实际数组元素的缓冲区,由于一般通过数组的索引获取元素,所以通常不需要使用这个属性。 |
import numpy as np
a = np.array([[1,2,3],[4,5,6]])
print (a.shape)
shape也可以用来改变数组的大小,但是一般习惯用reshape
import numpy as np
a = np.array([[1,2,3],[4,5,6]])
b = a.reshape(3,2)
print (b)
输出结果为:
[[1, 2]
[3, 4]
[5, 6]]
itemsize返回所占用的字节
import numpy as np
# 数组的 dtype 为 int8(一个字节)
x = np.array([1,2,3,4,5], dtype = np.int8)
print (x.itemsize)
1,创建指定形状指定数据类型的空数组使用numpy.empty,形状就是说要指定行和列
numpy.empty(shape, dtype = float, order = 'C')
import numpy as np
x = np.empty([3,2], dtype = int)
print (x)
numpy.ones
创建指定形状的数组,数组元素以 1 来填充:
import numpy as np
# 默认为浮点数
x = np.ones(5)
print(x)
NumPy 迭代器对象 numpy.nditer(a) 提供了一种灵活访问一个或者多个数组元素的方式,可以把多维数组以一维的方式一个一个输出
import numpy as np
x = np.arange(6).reshape([2,3])
for i in np.nditer(x):
print(i, end=", ")
print('\n')
cuda安装失败CUDA安装失败的解决经验_Jasmine_z_s的博客-CSDN博客_cuda安装失败
a.T就是对a进行转置,默认是按行访问,下面一个是按照行来访问一个是按照列来访问
import numpy as np
a = np.arange(0,60,5)
a = a.reshape(3,4)
print ('原始数组是:')
print (a)
print ('\n')
print ('以 C 风格顺序排序:')
for x in np.nditer(a, order = 'C'):
print (x, end=", " )
print ('\n')
print ('以 F 风格顺序排序:')
for x in np.nditer(a, order = 'F'):
print (x, end=", " )
输出结果为:
原始数组是:
[[ 0 5 10 15]
[20 25 30 35]
[40 45 50 55]]
以 C 风格顺序排序:
0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55,
以 F 风格顺序排序:
0, 20, 40, 5, 25, 45, 10, 30, 50, 15, 35, 55,
修改数组中的值nditer 对象有另一个可选参数 op_flags。 默认情况下,nditer 将视待迭代遍历的数组为只读对象(read-only),为了在遍历数组的同时,实现对数组元素值得修改,必须指定 read-write 或者 write-only 的模式x[...],x[...]=2*x,直接修改元素里面的值
import numpy as np
a = np.arange(0,60,5)
a = a.reshape(3,4)
print ('原始数组是:')
print (a)
print ('\n')
for x in np.nditer(a, op_flags=['readwrite']):
x[...]=2*x
print ('修改后的数组是:')
print (a)
print (a.flatten(order = 'F'))默认按照行展开print (a.ravel(order = 'F')),默认也是按照行展开,两个函数的功能是类似的,当然也是可以通过控制np.nditer(a, order = 'C')中nditer的参数来访问
原数组:
[[0 1 2 3]
[4 5 6 7]]
展开的数组:
[0 1 2 3 4 5 6 7]
翻转数组
numpy.transpose
numpy.transpose 函数用于对换数组的维度,格式如下:
numpy.transpose(arr, axes)
参数说明:
arr
:要操作的数组axes
:整数列表,对应维度,通常所有维度都会对换。
numpy.ndarray.T 类似 numpy.transpose:就是类似的一个是a.T,一个是np.transpose(a)上面很多重复的方法都是一样的都是点操作符来访问一个是函数形式访问,np.where(b==6)就是返回特定数值的索引,找出b中等于6的元素并返回下标,调用numpy.rollaxis(arr, axis, start)这个函数对轴进行翻滚其实等效为写为二进制后的一个移位,
如何理解呢,很简单,写出原数组的轴的索引: 000 001 010 011 100 101 110 111 调用 np.rollaxis(a,2)函数意思就是将2轴旋转至轴0的前面,轴序0,1,2变成1,2,0 操作等同于np.rollaxis(a,2,0)新的轴至原轴的索引可以表示为: 000 010 100 110 001 011 101 111
numpy.broadcast_to(数列,形状)
import numpy as np a = np.arange(4).reshape(1,4) print ('原数组:') print (a) print ('\n') print ('调用 broadcast_to 函数之后:') print (np.broadcast_to(a,(4,4)))
numpy.squeeze(a)从数组的形状中删除单维度条目,即把shape中为1的维度去掉只能把(1,3,3)变为(3,3),numpy.concatenate((a,b),axis=0),axis是0从纵轴拼接1是表示从行方向拼接,
import numpy as np a = np.array([[1,2],[3,4]]) print ('第一个数组:') print (a) print ('\n') b = np.array([[5,6],[7,8]]) print ('第二个数组:') print (b) print ('\n') # 两个数组的维度相同 print ('沿轴 0 连接两个数组:') print (np.concatenate((a,b))) print ('\n') print ('沿轴 1 连接两个数组:') print (np.concatenate((a,b),axis = 1))
numpy.split(被分割的数组,3)每三个分一组,numpy.split(被分割的数组,[4,5])表示从里面元素4和5的位置切
import numpy as np a = np.arange(9) print ('第一个数组:') print (a) print ('\n') print ('将数组分为三个大小相等的子数组:') b = np.split(a,3) print (b) print ('\n') print ('将数组在一维数组中表明的位置分割:') b = np.split(a,[4,7]) print (b)
resize和reshape功能一样,数组元素的添加和删除
append
将值添加到数组末尾 insert
沿指定轴将值插入到指定下标之前 delete
删掉某个轴的子数组,并返回删除后的新数组 unique
查找数组内的唯一元素 - append 函数返回的始终是一个一维数组,它的添加是整块整块的添加
import numpy as np a = np.array([[1,2,3],[4,5,6]]) print ('第一个数组:') print (a) print ('\n') print ('向数组添加元素:') print (np.append(a, [7,8,9])) print ('\n') print ('沿轴 0 添加元素:') print (np.append(a, [[7,8,9]],axis = 0)) print ('\n') print ('沿轴 1 添加元素:') print (np.append(a, [[5,5,5],[7,8,9]],axis = 1))
广播就是把数组转换为适合操作的模式,
numpy.insert(arr, obj, values, axis)如果没有指定轴的话会先展开为一维数组再来判断索引的位置
import numpy as np a = np.array([[1,2],[3,4],[5,6]]) print ('第一个数组:') print (a) print ('\n') print ('未传递 Axis 参数。 在插入之前输入数组会被展开。') print (np.insert(a,3,[11,12])) print ('\n') print ('传递了 Axis 参数。 会广播值数组来配输入数组。') print ('沿轴 0 广播:') print (np.insert(a,1,[11],axis = 0)) print ('\n') print ('沿轴 1 广播:') print (np.insert(a,1,11,axis = 1))
numpy.delete
numpy.delete 函数返回从输入数组中删除指定子数组的新数组。 与 insert() 函数的情况一样,如果未提供轴参数,则输入数组将展开。
Numpy.delete(arr, obj, axis),注意axis的删除轴和其他有点不一样axis=0表示从行方向删除,axis=1表示重列方向删除
import numpy as np a = np.arange(12).reshape(3,4) print ('第一个数组:') print (a) print ('\n') print ('未传递 Axis 参数。 在插入之前输入数组会被展开。') print (np.delete(a,5)) print ('\n') print ('删除第二列:') print (np.delete(a,1,axis = 1)) print ('\n')
numpy.unique(a)表示去除a中重复的元素,ndarray对象的内容可以通过索引或切片来访问和修改,与 Python 中 list 的切片操作一样,ndarray 数组可以基于 0 - n 的下标进行索引,切片对象可以通过内置的 slice 函数,并设置 start, stop 及 step 参数进行,从原数组中切割出一个新数组,import numpy as np a = np.arange(10) s = slice(2,7,2) # 从索引 2 开始到索引 7 停止,间隔为2 print (a[s]),不过常用冒号来切片,我们也可以通过冒号分隔切片参数 start:stop:step 来进行切片操作:
import numpy as np a = np.arange(10) b = a[2:7:2] # 从索引 2 开始到索引 7 停止,间隔为 2 print(b)
多维数组的切片也是同样的不过此时的起始变为行和列为单位
import numpy as np a = np.array([[1,2,3],[3,4,5],[4,5,6]]) print(a) # 从某个索引处开始切割 print('从数组索引 a[1:] 处开始切割') print(a[1:])
切片还可以包括省略号 …,表示切出整行或者整列
import numpy as np a = np.array([[1,2,3],[3,4,5],[4,5,6]]) print (a[...,1]) # 第2列元素 print (a[1,...]) # 第2行元素 print (a[...,1:]) # 第2列及剩下的所有元素
5.2matplot散点图绘制
import matplotlib.pyplot as plt import numpy as np import pandas as pd import os.path file_path = os.path.join("D:\\tmp\pythonProject1\data.csv") data = pd.read_csv(open(file_path,'r',encoding='utf-8'),sep='|') print(data.head()) data.plot(kind='scatter',x= '120',y='4') plt.show()
sns.FaceGrid,通过map来改变不同列的信息,超过两维的数组叫张量
import torch import numpy as np a=torch.empty([1,2]) # b=torch.from_numpy(a) print(a)
a=torch.randint(1,10,[3,3]),rand就有rand/rand_like,randint,randn是正态分布torch.rand_like(a)相当于torch.rand(a.size()),torch.randn(3,3)是一个均值为0方差为1的正态,torch.normal()可以自定义均值和方差,full就是全部都是的意思,arrange(起始元素,终止元素,等差)不设置默认为1,torch.linspace(0,10.step=10)就是0到10之间10等分,生产1的torch.ones(3,3),是3x3的全1矩阵,零矩阵是torch.zeros(3,3),对角矩阵torch.eye(3)或者torch.eye(3,3),torch.randperm(10),0到10重新打散随机生产一个数列 索引和切片 单个冒号表示取全部,冒号一边有数字:2表示从0取到2不包括2,两个冒号表示隔行或者隔列采样开始:结束:步长,三个点号可以用a[...]表示任意维度是多个冒号的改进,取出大于0.5的数mask=x.ge(0.5),x就是要处理的数据,使用torch.masked_select(x,mask)就可以筛选出来
import torch import numpy as np a=torch.rand(4,1,28,28) print(a.shape) b=a.unsqueeze(0).shape print(b)
shape是属性,其他的是方法
import torch import numpy as np a=torch.tensor([1.2,2.3]) print('a',a.shape) print('维度',a.dim()) print('大小',a.size())
a.unsqueeze(index).shape,这相当是在(b,c,w,h)增加维度后变为(b,c,w,h,g),而a.unsqueeze(index)是元素不变对里面的行和列进行变换a.t()方法只能适合用在2D,3D和4D都不合适,还要一个transpose函数,可以使用nd.dot(X,Y)
1,合并与拆分cat,stack,split是按照长度拆分,Chunk是按照数量进行拆分import torch import numpy as np a=torch.rand(4,32,8) b=torch.rand(5,32,8) c=torch.cat([a,b],dim=0).shape print(c)
第一维度是行,第二维度是列,进行拼接的必须是其他维度不相同,只有一个维度相同,stack和cat虽然都是连接的但是维度不一样stack会创建一个新的维度,连接相同的维度,维度必须相同
import torch import numpy as np a=torch.rand(32,8) b=torch.rand(32,8) c=torch.stack([a,b],dim=0).shape print(c)
split根据长度拆分[1,2,34,5]如果长度为2可以拆分得到两个,一个是根据要拆分得到的个数来拆分一个是按照len来拆分c.split(第一个参数是长度,第二个是维度),如果长度都一致的话就给一个2,如果不一致的话直接给个列表[2,3,1]就是先按照长度2,之后再按照长度3来拆分
import torch import numpy as np a=torch.rand(32,8) b=torch.rand(32,8) c=torch.stack([a,b],dim=0) aa,bb=c.split([1,1],dim=0) print(aa.shape,',',bb.shape)
chunk(2,dim=0)和split类似
3.矩阵的乘法就使用matmul,matmul多个维度的时候依然只去最后得两维,前面的维度不变,import torch import numpy as np a=torch.eye(2,2) b=torch.ones(2,2) c=torch.matmul(a,b) print(c)
a.pow(2)就是a的二次方,a**2也是a的二次方,a.sqrt()是开平方根,a.rsqrt()就是平方之后再开根,torch.log(a)这是以1为底的,torch.log2(a)以2为底的,torch.log10(a)以10为底的,近似值a.round()是四舍五入,a.floor()向下取整,a.ceil()向上取整,a.trunc()取a得整数部分,a.frac()取小数部分,torch.rand(2)表示均匀分布,grad.clamp(最小值,最大值)只有一个参数的时候表示最小值grad.clamp(10)小于10的变为10,即对Tensor中的元素进行范围过滤,不符合条件的可以把它变换到范围内部(边界)上,常用于梯度裁剪(gradient clipping),即在发生梯度离散或者梯度爆炸时对梯度的处理,实际使用时可以查看梯度的(L2范数)模来看看需不需要做处理:
w.grad.norm(2)
。 -
5.3torch的使用import torch grad = torch.rand(2, 3) * 15 # 0~15随机生成 print(grad.max(), grad.min(), grad.median()) # 最大值最小值平均值 print(grad) print(grad.clamp(10)) # 最小是10,小于10的都变成10
统计属性
1-范数就是所有元素的绝对值之和a.norm(1),2-范数就是所以元素绝对值的平方再开根号a.norm(2),求某一个维度上的番薯a.norm(1,dim=1),a的一维度上的1范数,[2,3]在第一维度上得到的范数就是2大小的,1)均值、累加、最小、最大、累积b.mean(), b.sum(), b.min(), b.max(), b.prod(),a.argmax(),返回的是最大值的索引,但是返回的是打平后的如果希望打平需要指明维度,a.argmax(dim=1),使用keep和keepdim可以同时获得最大值和维度信息keepdim=true表示保持维度信息不变,import torch a=torch.rand(4,10) c=a.max(dim=1,keepdim=True) print(c)
a.topk()可以比我们的a.max()返回更多的参数,可以返回最大值此大值,a.top(返回几个大值,维度,控制最大最小)
import torch a=torch.rand(4,10) print('原来a的值',a) c=a.topk(3,dim=1,largest=True) print(c)
torch.where(condition,x,y)用x,y中的数据来构建一个新的矩阵,1表示数据来源A中2表示数据来源于B中
import torch a=torch.rand(2,2) print('原来a的值',a) b=torch.zeros(2) c=torch.eye(2) d=torch.where(a>0.5,b,c) print(d)
gather(表,dim=0,查表项)就是用查表项来获得矩阵
-
六,深度学习知识方面 -
6.1梯度
-
1,导数就是一个变化率,梯度是一个向量,梯度的长度反映了某一种变化趋势,长度越长就越陡峭,也就是增加的速率,方向是朝着函数增长的方向就是从极小值指向极大值,下面就利用梯度来求极小值
凸函数可以找到一个全局最优解,步长太大可能直接就会跨过全局最优解,通过惯性来逃离局部极小值,就是找到局部极小值的方向和原来梯度的方向来决定下一方向,激活函数及其梯度
激活函数是不可导的函数,为了解决这个问题提出了sigmoid/Logistic函数
sigmoid函数在-∞,和+-∞会出现梯度弥散import torch a=torch.linspace(-100,100,10) print(torch.sigmoid(a))
tanh函数实现import torch a=torch.linspace(-1,1,10) print(torch.tanh(a))
整形线性单元Rectified Linear Uint ReLU,z<0就是不响应(梯度是0),z>0就是响应(梯度是1),可以保持梯度不变
import torch a=torch.linspace(-1,1,10) print(torch.relu(a))
6.2loss函数
-
.LOSS及其梯度,自动求导假设预测模型是pred=xw+b,x初始化为1,w初始化为[2],b=0import torch from torch.nn import functional as F # pred = x*w x = torch.ones(1) # dim=1,长度为1,值初始化为2 w = torch.full([1], 2,dtype=float,requires_grad=True) # 实际输出 y = torch.ones(1) mse = F.mse_loss(x * w, y) par=torch.autograd.grad(mse,[w]) print(par)
使用
loss.backward()
使用loss对象的
backward()
方法,让PyTorch做反向的传播,将需要grad信息的Tensor求出这个loss关于它的导数出来, -
mse.backward() print(w.grad),w1.grad,w2.grad
sigmoid的最后结果之和不一定等于1,softmax的最后结果为1,因为计算梯度时outputs需为标量(未指明grad_outputs或grad_outputs为None时)import torch from torch.nn import functional as F # pred = x*w a = torch.rand(3,requires_grad=True) p=F.softmax(a,dim=0) p.backward() #softmax的这个求导要指明维度只能是p[i],不能是p par1=torch.autograd.grad(outpusp[1],[a],retain_graph=True) print(par1)
网络上标号的意思
import torch from torch.nn import functional as F # pred = x*w x= torch.randn(1,10) w=torch.randn(1,10,requires_grad=True) o=torch.sigmoid(torch.matmul(x,w.t())) loss=F.mse_loss(torch.ones(1,1),o) loss.backward() print(w.grad)
多层输出的情况
import torch from torch.nn import functional as F x = torch.randn(1, 10) w = torch.randn(2, 10, requires_grad=True) # 对输出用sigmoid激活 o = torch.sigmoid(x @ w.t()) print("输出值:", o) # 计算MSE loss = F.mse_loss(torch.ones(1, 2), o) print("损失:", loss) # 计算梯度 loss.backward() print("损失对w的导数:", w.grad)
链式求导法则
import torch from torch.nn import functional as F 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] print('resulr:',dy2_dy1*dy1_dw1) dy2_dw1=torch.autograd.grad(y2,[w1],retain_graph=True)[0] print(dy2_dw1)
反向传播
2D函数优化
np.arange(起始,终止,步长),meshgrid的理解import numpy as np import matplotlib.pyplot as plt %matplotlib inline m, n = (5, 3) x = np.linspace(0, 1, m) y = np.linspace(0, 1, n) X, Y = np.meshgrid(x,y) 查看向量x和向量y x out: array([ 0. , 0.25, 0.5 , 0.75, 1. ]) y out: array([ 0. , 0.5, 1. ]) 查看矩阵X和矩阵Y X out: array([[ 0. , 0.25, 0.5 , 0.75, 1. ], [ 0. , 0.25, 0.5 , 0.75, 1. ], [ 0. , 0.25, 0.5 , 0.75, 1. ]]) Y out: array([[ 0. , 0. , 0. , 0. , 0. ], [ 0.5, 0.5, 0.5, 0.5, 0.5], [ 1. , 1. , 1. , 1. , 1. ]])
转换视角进行观察,通过设置view_init(elev,azim)两个参数变化时,观察图像的变化,plot_surface 3D绘图函数,就相当于获得this,softmax是一个函数用于分类问题可以让所有的概率之和是1,并且拉大差距,
import numpy as np from mpl_toolkits.mplot3d import Axes3D from matplotlib import pyplot as plt import torch 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() # [1., 0.], [-4, 0.], [4, 0.] x = torch.tensor([4., 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()))
熵越高代表越稳定,没有惊喜度,因为加了一个负号,
import torch from torch.nn import functional as F a=torch.full([4],1/4) b=a*torch.log2(a) c=-b.sum() print(c)
(0-1)分类用KLdivergence(散度)来衡量,如果实际分类为1,真实也是1也就是p=q,kldivergence=0,二分类问题的目标函数,p是真实参数,Q是经过模型得到,
对于分类问题不使用MSE,而使用crossEntorpy,,没有经过softmax的叫做logit
F.cross_entropy()=softmax+log+null_loss,cross_entorpy就是一个散度,区分度import torch from torch.nn import functional as F x=torch.randn(1,784) w=torch.randn(10,784) logits=x@w.t() par1=F.cross_entropy(logits,torch.tensor([3])) print(par1) #方法二 pre=F.softmax(logits,dim=1) pre_log=torch.log(pre) par2=F.nll_loss(pre_log,torch.tensor([3])) print(par2)
这三个函数的作用是先将梯度归零(optimizer.zero_grad()),然后反向传播计算得到每个参数的梯度值(loss.backward()),最后通过梯度下降执行一步参数更新(optimizer.step())
多分类问题实战,0-9的目标概率识别
import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim from torchvision import datasets, transforms batch_size=200 learning_rate=0.01 epochs=10 train_loader = torch.utils.data.DataLoader( datasets.MNIST('../data', train=True, download=True, transform=transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ])), batch_size=batch_size, shuffle=True) test_loader = torch.utils.data.DataLoader( datasets.MNIST('../data', train=False, transform=transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ])), batch_size=batch_size, shuffle=True) 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) torch.nn.init.kaiming_normal_(w1) torch.nn.init.kaiming_normal_(w2) torch.nn.init.kaiming_normal_(w3) 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 optimizer = optim.SGD([w1, b1, w2, b2, w3, b3], lr=learning_rate) 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() # print(w1.grad.norm(), w2.grad.norm()) optimizer.step() if batch_idx % 100 == 0: print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format( epoch, batch_idx * len(data), len(train_loader.dataset), 100. * batch_idx / len(train_loader), loss.item())) test_loss = 0 correct = 0 for data, target in test_loader: data = data.view(-1, 28 * 28) logits = forward(data) test_loss += criteon(logits, target).item() pred = logits.data.max(1)[1] correct += pred.eq(target.data).sum() test_loss /= len(test_loader.dataset) print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format( test_loss, correct, len(test_loader.dataset), 100. * correct / len(test_loader.dataset)))
-
七、卷积神经网络
-
7.1全连接层
-
,numerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中,range(start, stop[, step]),
torch.nn.functional.
linear
(input, weight, bias=None),使用inplace=True
可以把多出来的内存消耗省下来,CLASStorch.nn.
Sequential
(*args: Any),Sequential容器,两个API函数API和类API,L类API可以是用大写定义的比如ReLu这就需要先定义对象再调用,另外一类是函数的一般是小写比如relu,Python 字典(Dictionary) items() 函数以列表返回可遍历的(键, 值) 元组数组,就是说返回两个参数不仅仅只有each,argmax()就是求最大值的下标.
TensorboardX一个可视化面板,主要是对学习过程做监听,需要from tensorboardX import SummarWriter对numpy数据进行操作如果接收的是tensor要转为numpy,但是visdom又比Tensorboard好,这个东西弄完之后要在cmd中开启监听服务器命令是python -m visdom.server之后把得到的端口放到网页上打开 http://localhost:8097,
测试的目的是为了检测有没有overfiting,我们做测试让曲线去更好的拟合,比如我门现在要曲线往下走,但是测试的时候发现她往上走了,这个时候我们可以用前一次测试的(w,b)这次的舍弃,就是说test可以用来做我们模型的挑选,如果对数据集做划分的话,有TrainSet,ValSet,TestSet,就变为了1用来训练,2用来挑选参数,3用来给用户测试,第一段和第二段一般不这么划分,一般都是K-fold,数据集的划分import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim from torchvision import datasets, transforms batch_size=200 learning_rate=0.01 epochs=10 train_db = datasets.MNIST('../data', train=True, download=True, transform=transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ])) train_loader = torch.utils.data.DataLoader( train_db, batch_size=batch_size, shuffle=True) test_db = datasets.MNIST('../data', train=False, transform=transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ])) test_loader = torch.utils.data.DataLoader(test_db, batch_size=batch_size, shuffle=True) print('train:', len(train_db), 'test:', len(test_db)) train_db, val_db = torch.utils.data.random_split(train_db, [50000, 10000]) print('db1:', len(train_db), 'db2:', len(val_db)) train_loader = torch.utils.data.DataLoader( train_db, batch_size=batch_size, shuffle=True) val_loader = torch.utils.data.DataLoader( val_db, batch_size=batch_size, shuffle=True)
7.2如何减轻overfiting,
-
使用regularization,来处理动量与学习率衰减,动量就是惯性,动量的调整是在SGD中通过momentum来调整,learning rate decay就是把学习率衰减到0,连续10次,值都没有变动可以调用torch.optim.Ir_scheduler.ReduceLROnPlateau()来减小学习率,使用scheduler.step(loss_val)来监听loss的值,之后会自己判断是否达到了高原,到了高原之后学习率会减半,early stoping就是过拟合之后偏离了我们的目标点之后的停止训练点,dropout是让让我们使用的w参数尽可能的少,这个参数也是为了减少overfiting的,就是思路就是有的连接层会断,使用函数torch.nn.Dropout(p=dropout),drop_out=1,表示此连接可能会断
import torch from torch.nn import functional as F #print(torch.__version__) net_droppedd=torch.nn.Sequential( torch.F.Linear(784,200), torch.F.Dropout(0.5), torch.F.ReLu() )
丢弃是训练的时候可以用,但是在测试的时候是不可以用的,在测试之间需要先使用net_dropped.eval(),使网络连接回到正常状态,Stochastic Gradient Decent和普通梯度的区别是什么呢,也就是说这个的数据集是经过采样后的数据集,而原来的是没有经过采样后的数据集,L1正则化和L2正则化的详细直观解L1正则化和L2正则化的详细直观解释_这孩子谁懂哈的博客-CSDN博客_l1正则化
-
7.3,卷积
-
我们处理数据借鉴人类肉眼识景物的过程都是从最关心得开始开,然后才能关注到其他的东西 -
layer=nn.Conv2d(输入,核个数,核尺寸,步长,填充)调用的时候直接layer(x),向下采样就是隔行采样,池化也就是采样的意思,在库中如何实现layer=nn.MaxPool2d(池化窗口大小,strid=2),F.avg_pool2d(输入,池化窗口大小,stride=2),向上采样会复制最近的一个值,达到放大的一个效果,通过函数F.interpolate(x,scale_factor=2,mode=''nearest)实现,ReLu函数通过layer=nn.ReLU(inpace=True),让数据映射到(0,1)空间使用nn.BatchNorm2d(通道数),使用layer.eval()来切换是哪一种训练模式,
加载数据--->建立模型--->训练和测试---->transfer learning
__len__获取样本的长度,__getitem__获得样本,自定义数据集的母类,初始化把数据加载进来,只要实现了一个数据的读取DataLoader实现数据的重复读取,loader=Dataloader(对象db,bach_size样本大小,shuffle=True是否打散),只要创建对象就会调用构造函数,就会返回一个读取到的第一行数据,样本大小就是一次拿多少,model.parameters()可以看这个模型有多少个
vars() 函数返回对象object的属性和属性值的字典对象。os.path.join()函数:连接两个或更多的路径名组件,os.environ.get(里面放环境的名字)是python中os模块获取环境变量的一个方法,train loopz训练循环,抽取样本x和对应的目标y,在x上运行网络得到预测值叫前向传播,计算loss更新权值,
out = model(inputs) ls.append(out.detach().cpu().numpy())
out是device:CUDA得到的CUDA tensor。关于detach()的官方文档如下:,相同维度的数组合并使用np.concatenate函数
过采样和欠采样,采样信号高于最高信号的两倍被称为过采样,低于就称为采样,频域是可以进行欠采样的只要保证采样信号的带宽大于原始信号带宽的两倍,MSE叫均方误差估计,python staticmethod 返回函数的静态方法。
该方法不强制要求传递参数,如下声明一个静态方法:
class C(object): @staticmethod def f(arg1, arg2, ...): ...
以上实例声明了静态方法 f,从而可以实现实例化使用 C().f(),当然也可以不实例化调用该方法 C.f(),j静态方法的函数传递的时候可以不用调用参数,先说一下怎么可视化打开cmd直接切换到对应的目录下,输入jupyter notebook 就可以打开,切换到日志目录下,激活虚拟环境activate enviriment 然后tensorboar logdir=vision_0
-
训练集:学习样本数据集,通过匹配一些参数来建立一个分类器。建立一种分类的方式,主要是用来训练模型的。
-
验证集:对学习出来的模型,调整分类器的参数,如在神经网络中选择隐藏单元数。验证集还用来确定网络结构或者控制模型复杂程度的参数。
-
测试集:主要是测试训练好的模型的分辨能力(识别率等)
使用常量
需要先引入定义好的 const 对象,再给 const 对象添加常量,查找函数被谁调用OrderedDict(),
当数据集比较小的时候我们可以通过裁剪,旋转rotate,缩放scale,还有随机的裁剪,没有满足尺寸的地方用0或者1来填充还有添加白噪声,等复制数据集来使得数据量变大,可以调用函数实现transform.RandomRotation(15),范围就是-15度到15度,缩放可以通过函数transforms.Resize([32,32])来实现,随机的裁剪调用transforms.RandomCrop([28,28]),transform是torchvison这个包里面提供的,__call__方法使得我们可以像调用函数一样调用类,只要我们调用类函数名的时候就会执行__call__里面的东西,
输入32x32----->卷积层------->池化------->卷积层------>池化
nn.sequential()即使一个容器,将一些参数按照构造函数中传递的顺序添加到网络,也可以传入一个有序的字典orderdict.激活函数用在输入的处理中将数据归一化到(0到1)方便处理,最后需要显示的时候再还原,激活函数的另外一个作用就是把网络变为非线性,比如下面的问题就没有办法通过线性模型的解决,激活函数就加载隐含层和输出层之间,通过这一种方法,慢慢的就可以学习到图三的曲线, -
class torch.nn.Linear(in_features,out_features,bias = True )[来源]
对传入数据应用线性变换:y = A x+ b
参数:
in_features - 每个输入样本的大小
out_features - 每个输出样本的大小
bias - 如果设置为False,则图层不会学习附加偏差。默认值:True
x.view(x.size(0), -1)这句话是说将第二次卷积的输出拉伸为一行,这句代码中的上一句中x的执行结果为:50*32*7*7个数
enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中
关于x.item()用法:
文档中给了例子,说是一个元素张量可以用item得到元素值,请注意这里的print(x)和print(x.item())值是不一样的,一个是打印张量,一个是打印元素:x = torch.randn(1) print(x) print(x.item()) #结果是 tensor([-0.4464]) -0.44643348455429077
-
八、常见的图像重建
-
8‘1图像超分辨率重建
-
,一种是插值的方法,亚当像素就是不仔细看以为已经没有像素了,通过亚像素卷积的方式,实现从低分辨图到高分辨图的重构,具体如下所示,还是通过补0操作来放大数据,MSE作为目标函数来训练模型,子标题比较多就说明比较重要,先重点抓取重点内容,然后看图片的表格,超分辨率重建的一个流程就是1.选取样本2.采样变为低分辨率图片,3.对低分辨率图片进行放大(一般采用插值的方法)和复原
图像修复模型
图像放大的另外一种方法,子像素卷积,是一种巧妙的图像和图像特征图放大的方法,又叫像素清洗,该方法后续也被应用在了SRResNet和SRGAN算法中,常见的扩尺度方法有直接上采样,双线性插值,反卷积等等,以及子像素卷积,这里的放大是通过扩充通道,之后再把通道合并实现,JS就是GAN的D,WGAN的是f,是为解决GAN训练不稳定的问题,
迁移学习就是当数据量比较小的时候,用一个数据分布和我们现在的分布类似的一个分布训练好的一个模型来作为我们的先验模型,在原来的基础上把我们的数据再放到这个模型中训练,得到一个好的模型
无监督学习有两个全连接层,中间有个neck可以用来升维和降维
自监督学习就是把输入图片旋转一个角度放到网络里面训练,最后让网络判断一下旋转了多少度,网络为了预测一个图片的旋转会提取图片的特征,自监督学习的核心任务是如何给输入数据自动生成标签,对于自监督学习来说,存在三个挑战:主要是无标签数据特征提取,有效性评测
- 对于大量的无标签数据,如何进行表征/表示学习?
- 从数据的本身出发,如何设计有效的辅助任务 pretext?
- 对于自监督学习到的表征,如何来评测它的有效性?
自监督学习有三种方法1. 基于上下文(Context based) 2. 基于时序(Temporal Based)3. 基于对比(Contrastive Based)
提出了一种拼图的方式一张图片分为9个部分然后打乱,丢到网络中让网络提取特征之后预测这9小块的位置
1.首先介绍基于上下文的
找两个图片的相关度如上面的公式1
还有自监督学习做的另外一个事情是什么呢,就是把一个完整的图片扣去一部分,用剩下的图片预测被扣掉的部分,还有另外一种是输入图片的灰度信息,预测图片的彩色,解耦表示就像并联一样首先找出影响数据关键因素,这一关键因素对应着图片上的某些特征改变这个特征可以唯一的改变图片的表示解耦表示学习旨在对影响数据形态的关键因素(factor)进行建模,使得某一关键因素的变化仅仅引起数据在某项特征上的变化,而其他的特征不受影响。例如,人脸的特征有肤色、发型、五官等等,如果我们成功对人脸进行了解耦表示,则我们可以通过改变其中对应的关键因素(可以是低维隐变量的某一维)来改变一张人脸的肤色,而该人脸的发型、五官等其他特征维持不变,数据增广详细链接数据增广之详细理解 - 知乎,数据增广是深度学习中常用的技巧之一,主要用于增加训练数据集,让数据集尽可能的多样化,使得训练的模型具有更强的泛化能力.现有的各大深度学习框架都已经自带了数据增广,但是平时在用的使用只是直接调用了对应的接口函数,而没有进行详细的分析.在实际应用中,并非所有的增广方式都适用当前的训练数据,你需要根据自己的数据集特征来确定应该使用哪几种数据增广方式.这篇文章的目的是为了更好地理解各种增广方式及其背后的真正原理.
目前数据增广主要包括:水平/垂直翻转,旋转,缩放,裁剪,剪切,平移,对比度,色彩抖动,噪声等
2.基于时序的
之前介绍的是基于图像信息本身,而现在要介绍的方法是基于图片间的这样的化,就是说是用在视频中,我们认为视频中相邻帧之前的图像是相关的,而相隔较远的是不相关的,那么对于一个物体追踪框在不同帧的特征应该是相似的,而对于不同物体的追踪框中的特征应该是不相似的,而对于不同物体的追踪框中的特征应该是不相似的,设计一个模型,来判断当前的视频序列是否是正确的顺序。
3.基于对比度
就是基于对比度约束,通过学习两个事物的相似和不相似进行编码来构建表征,基于对比度的约束,我们首先要构建一个正样本和负样本,样本和正样本之间的距离大于样本和负
x 通常也称为 「anchor」数据,为了看x和正负样本之间的关系,首先构造距离函数,然乎构造softmax()分类器,以正确区分正样本和负样本,相似性度量函数将较大的值分配给正项,将较小的值分配给负向
我们的模型需要干什么呢,就是通过全局特征和正样本判断图像是否来自于同一图像,模型需要分类全局特征和局部特征是否来自同一图像。所以这里 x 是来自一幅图像的全局特征,正样本是该图像的局部特征,而负样本是其他图像的局部特征CPC是一个基于对比度约束的自监督学习框架,主要是可以应用于能够以有序序列表示的任何形式的数据:文本、语音、视频、甚至图像(图像可以被视为像素或块的序列),CPC的主要思想就是基于过去的信息预测的未来数据,通过采样的方式进行训练
常规操作是通过各种辅助任务/代理任务(pretext task/auxiliary task)从无监督数据中挖掘监督信息,提高学习表征(representation什么是惩罚项?
在机器学习中,我们常看到再损失函数后面会跟另外一个额外项,这个便是传说中的惩罚项。(可以理解为,是损失函数的补充调节)
一般情况分为两种,L1 正则化,和L2正则化。(也可以叫做范数,英文是 L1-norm),之所以翻译作正则化,是因为这个解决方案在学术上称作 regularization .
L_1L1正则化:这个是指权值向量ww 中各个元素的绝对值之和。通常表示为 ||w||_1∣∣w∣∣1 。可以产生稀疏权值矩阵,可以用于特征选择。(所以就相当于,只有少量参数决定结果,因此抗噪能力比较强,在实际应用中,可解释性也比较强)
L_2L2正则化:这个是指权值向量ww 中各元素的平方和,然后再求平方根 {||w||^2}∣∣w∣∣2。可以防止过拟合(倾向于得到近似0的参数估计,几乎用到所有的变量)。一定程度上L1也可以防止过拟合。
什么又是稀疏矩阵呢?
稀疏矩阵是指很多元素为 0 ,只有少数元素是非零值的矩阵。即得到的线性回归模型的大部分系数都是 0 。在机器学习中,通常情况数据特征非常多,比如文本处理时,可能特征会有上万个。
这时候在预测或者分类时,实际上只有少数特征对这个模型有贡献,绝大部分特征是没有贡献或者贡献较小。此时我们就可以只关注稀疏矩阵中的非零值对应的特征。
详细参照在机器学习中,什么是惩罚项?惩罚项有什么作用?-书生吴小帅
均值方差等方差、协方差、标准差、均方差、均方根值、均方误差、均方根误差对比分析_cqfdcw的博客-CSDN博客_均方根值C = torch.cat( (A,B),0 ) #按维数0拼接(竖着拼) C = torch.cat( (A,B),1 ) #按维数1拼接(横着拼)
A,B
在Python中,函数其实是一个对象:
>>> f = abs
>>> f.__name__
'abs'
>>> f(-123)
由于 f 可以被调用,所以,f 被称为可调用对象。
所有的函数都是可调用对象。
一个类实例也可以变成一个可调用对象,只需要实现一个特殊方法call()。
我们把 Person 类变成一个可调用对象:
class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
def __call__(self, friend):
print 'My name is %s...' % self.name
print 'My friend is %s...' % friend
现在可以对 Person 实例直接调用:
>>> p = Person('Bob', 'male')
>>> p('Tim')
My name is Bob...
My friend is Tim...
缩小图像(或称为下采样(subsampled)或降采样(downsampled))的主要目的有两个:1、使得图像符合显示区域的大小;2、生成对应图像的缩略图。
放大图像(或称为上采样(upsampling)或图像插值(interpolating))的主要目的是放大原图像,从而可以显示在更高分辨率的显示设备上。对图像的缩放操作并不能带来更多关于该图像的信息, 因此图像的质量将不可避免地受到影响。然而,确实有一些缩放方法能够增加图像的信息,从而使得缩放后的图像质量超过原图质量的。
下采样原理:对于一幅图像I尺寸为M*N,对其进行s倍下采样,即得到(M/s)*(N/s)尺寸的得分辨率图像,当然s应该是M和N的公约数才行,如果考虑的是矩阵形式的图像,就是把原始图像s*s窗口内的图像变成一个像素,这个像素点的值就是窗口内所有像素的均值:
上采样原理:图像放大几乎都是采用内插值方法,即在原有图像像素的基础上在像素点之间采用合适的插值算法插入新的元素。
无论缩放图像(下采样)还是放大图像(上采样),采样方式有很多种。如最近邻插值,双线性插值,均值插值,中值插值等方法
a.view(-1)将数据打散为1维
import torch
a = torch.randn(3,5,2,2)
print(a)
print(a.view(-1))
np.random.seed(0)的作用是使得每次预测的产生的随机数是相同的,因为有的时候随机数不同,里面的参数就是起始的一个位置,