# MLNEW **Repository Path**: binfly/MLNEW ## Basic Information - **Project Name**: MLNEW - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-08-05 - **Last Updated**: 2021-08-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # MLNEW 重新学习机器学习~加油 保持好,学习的激情与深度,加油 # onnx.js 模型在线学习:Pytorch--》onnx 看到的案例: ~~如何运行PyTorch模型的浏览器ONNX.js https://www.youtube.com/watch?v=Vs730jsRgO8实现:~~ 重新找了一个: https://www.youtube.com/watch?v=Vs730jsRgO8 实现: ![1629966907214.png](img/1629966907214.png) onnx的操作手册: ![1629970184396.png](img/1629970184396.png) ![1629970232346.png](img/1629970232346.png) 这个不支持 ![1630025547641.png](img/1630025547641.png) code: (本地路径:**pytorch\to_onnx\videolearn**) 文件夹:github中的代码地址: ``` https://github.com/elliotwaite/pytorch-to-javascript-with-onnx-js ``` 大致明白了思路过程,还有要看代码,加油~ my: ```apache #实现 ``` # 人脸识别 输入图片,显示名字 看了看opencv4nodejs上面的,与pytorch是两个不同的形式,想想还是用pytorch的来吧: pytorch训练模型, onnx.js加载模型输出结果, 在进行3d互动操作 打开手机/网页摄像头, 输入图片,显示名字,显示名字 路线就是 opencv截取视频图片, pytorch训练模型, onnx.js加载模型输出结果 在进行3d互动操作 ## Pytorch 人脸识别 我想,先这样,一步步来,先做python 环境下的,人脸识别,把人脸识别出来, 再做,java或者js的线上问题 简单搜索了一下,想想, 1.可以先使用,我现有的方法resnet18: 把图片放上去,数据增强,训练,验证,测试 最后,看看给一直图片,看看给的预测值 加油,明天就这么来: 理了理思路,可以先这么做,看看如何把pytorch的转换为onnx的模型--再到java运行, 试试如何做实时计算的 2.下一步,再用特征点什么的方法 # Pytorch 昨天很高兴,看到了这个,现在重新学习机器学习方面或者说人工智能方面的吧,通过不断的锤炼,做出好用的产品 ## pytorch初步: 看着这个进行学习吧,加油: https://www.bilibili.com/video/BV1U54y1E7i6 1.安装 ![20210806111825.png](./img/20210806111825.png) (1)Anacoda5.3.1---到清华镜像下载 Anacoda安装目录下面就有python.exe 查看python版本 ![20210806142714.png](./img/20210806142714.png) (2)cuda版本 查看: https://blog.csdn.net/zz2230633069/article/details/103725229 安装包下载地址(先装cuda10吧): https://developer.nvidia.com/cuda-10.0-download-archive 安装注意点: 自定义安装: https://www.pianshen.com/article/4550345762/ ![20210806134943.png](./img/20210806134943.png) 运行nvcc -v出现 nvcc fatal : No input files specified; use option --help for more information nvcc -V 就可以了 (3)安装pytorch: conda install pytorch==1.0.0 torchvision==0.2.1 cuda100 -c pytorch https://pytorch.org/get-started/previous-versions/ (4)pycharm下载:选择社区免费版本下载 https://www.jetbrains.com/zh-cn/pycharm/download/#section=windows ![20210806135736.png](./img/20210806135736.png) (5) 在vscode中运行py文件: 直接安装python插件,运行,ctrl+alt+n,运行 ![20210806172019.png](./img/20210806172019.png) ![20210806144904.png](./img/20210806144904.png) 对py文件自动补全: https://blog.csdn.net/XU18829898203/article/details/106042078 ![20210806171855.png](./img/20210806171855.png) 对vscode中python执行, (1)进行settings.json中配置: // 设置Python的路径 "python.pythonPath": "C:/ProgramData/Anaconda3/python", "python.terminal.executeInFileDir": true, //万能运行环境 "code-runner.executorMap": { //"python": "set PYTHONIOENCODING=utf8 && C:/ProgramData/Anaconda3/python" "python": "set PYTHONIOENCODING=utf8 && python", }, // 设置Python的代码格式化 "python.formatting.provider": "yapf", // 设置Python的代码检查 "python.linting.flake8Path": "pycodestyle", "python.autoComplete.extraPaths": [ "C:/ProgramData/Anaconda3/", "C:/ProgramData/Anaconda3/Lib/", "C:/ProgramData/Anaconda3/Lib/site-packages/", "C:/ProgramData/Anaconda3/DLLs/" ], "python.autoComplete.addBrackets": true, "notebook.cellToolbarLocation": { "default": "right", "jupyter-notebook": "left" }, "python.analysis.extraPaths": [ "C:/ProgramData/Anaconda3/", "C:/ProgramData/Anaconda3/Lib/", "C:/ProgramData/Anaconda3/Lib/site-packages/", "C:/ProgramData/Anaconda3/DLLs/" ], "python.analysis.completeFunctionParens": true, "python.linting.pylintPath": "C:\\ProgramData\\Anaconda3\\pkgs\\pylint-2.1.1-py37_0\\Scripts\\pylint", "code-runner.fileDirectoryAsCwd": true, (2)在.vscode中添加launch.json文件,并配置: { "configurations": [ { "cwd":"${fileDirname}" } ] } 解决相对路径问题: ![20210809092450.png](./img/20210809092450.png) (6) 安装成功验证: print(torch.__version__) #torch 版本 print(torch.cuda.is_available()) #是否支持gpu # 基础概念 # 张量 torch.tensor https://zhuanlan.zhihu.com/p/265394674 Tensor 的概念 Tensor 中文为张量。 张量的意思是一个多维数组,它是标量、向量、矩阵的高维扩展。 标量可以称为 0 维张量,向量可以称为 1 维张量,矩阵可以称为 2 维张量,RGB 图像可以表示 3 维张量。你可以把张量看作多维数组。 ![20210809094100.png](./img/20210809094100.png) Tensor 创建的方法 直接创建 Tensor torch.tensor() torch.tensor(data, dtype=None, device=None, requires_grad=False, pin_memory=False) data: 数据,可以是 list,numpy dtype: 数据类型,默认与 data 的一致 device: 所在设备,cuda/cpu requires_grad: 是否需要梯度 pin_memory: 是否存于锁页内存 代码示例: arr = np.ones((3, 3)) print("ndarray的数据类型:", arr.dtype) # 创建存放在 GPU 的数据 t = torch.tensor(arr, device='cuda') t= torch.tensor(arr) print(t) 输出为: 索引与切片 ![20210809143446.png](./img/20210809143446.png) ![20210809144314.png](./img/20210809144314.png) 间隔索引: ![20210809144803.png](./img/20210809144803.png) index_select--?这个在想想看吧 ![20210809155055.png](./img/20210809155055.png) ...: ![20210809155539.png](./img/20210809155539.png) ![20210809155817.png](./img/20210809155817.png) ![20210809155926.png](./img/20210809155926.png) # 维度变换: ![20210809160057.png](./img/20210809160057.png) ![20210809160642.png](./img/20210809160642.png) ![20210809161718.png](./img/20210809161718.png) 维度扩张: unsqueeze(正数):在前面加上维度: ![20210809161959.png](./img/20210809161959.png) 维度删减: ![20210809162547.png](./img/20210809162547.png) 维度扩展:设置为-1时候,不变换 ![20210809163006.png](./img/20210809163006.png) repeat: ![20210809172853.png](./img/20210809172853.png) 矩阵转置:.t ![20210809173100.png](./img/20210809173100.png) Transpose ![20210810093056.png](./img/20210810093056.png) permute:--维度转换,提前或者后移: ![20210810093342.png](./img/20210810093342.png) Broadcast自动扩展: Expand , without copying data 维度变换后,相加: ![20210810094650.png](./img/20210810094650.png) a = torch.rand(4, 32, 14, 14) b = torch.rand(1, 32, 1, 1) c = torch.rand(32, 1, 1) #b [1, 32, 1, 1]=>[4, 32, 14, 14] print((a + b).shape) print((a+c).shape) 拼接/合并: torch.cat ![20210810110222.png](./img/20210810110222.png) ![20210810110907.png](./img/20210810110907.png) torch.stack: ![20210810111232.png](./img/20210810111232.png) 拆分: ▪ Split 按长度进行拆分 aa, bb = c.split(1, dim=0) print(aa.shape, bb.shape) ![20210810112543.png](./img/20210810112543.png) ▪ Chunk 按数量 进行 拆分 aa, bb = c.chunk(2, dim=0) # 按2个数量 进行拆分---》拆分成两个 print(aa.shape, bb.shape) 基本运算: ![20210810113215.png](./img/20210810113215.png) 加减乘除: ![20210810113551.png](./img/20210810113551.png) 矩阵乘法:.matmul @ ![20210810113829.png](./img/20210810113829.png) 示例分析: 把4x784 转换为4x512矩阵, --可以把神经网络学习理解tensor的不断变换的过程--tensor flow的流动 ![20210810114601.png](./img/20210810114601.png) ![20210810120000.png](./img/20210810120000.png) 次方运算:pow ![20210810133225.png](./img/20210810133225.png) 幂次方:exp,log ![20210810134316.png](./img/20210810134316.png) 其他: ![20210810134437.png](./img/20210810134437.png) torch.clamp 将输入input张量每个元素的夹紧到区间 [min,max]内,并返回结果到一个新张量。 a = torch.randint(low=0, high=10, size=(10, 1)) print(a) a = torch.clamp(a, 3, 9) # 会把低于3的变为3,把高于9的变为9,数组数量不变 print(a) 统计属性: ![20210810140737.png](./img/20210810140737.png) ![20210810141559.png](./img/20210810141559.png) Tensor求和以及按索引求和:torch.sum() torch.Tensor.indexadd() Tensor元素乘积:torch.prod(input) --- 对Tensor求均值、方差、极值: torch.mean() torch.var() torch.max() torch.min() dim,keepdim:维度, 求 max,返回指定维度的最大值,及 索引。 ## argmax:返回指定维度的最大值的索引 argmax:返回指定维度的最大值的索引。 ![20210810145541.png](./img/20210810145541.png) topk kthvalue: ![20210810150239.png](./img/20210810150239.png) 比较: ![20210810150722.png](./img/20210810150722.png) 其他: where: ![20210810151412.png](./img/20210810151412.png) gather:收集--就是通过索引号,取值 ![20210810161134.png](./img/20210810161134.png) # 梯度:-----重要 什么是梯度:所有偏微分的总和。偏微分:自变量的导数 ![20210810161750.png](./img/20210810161750.png) 梯度是有长度,及方向的向量;表现了f(x,y) 在x,y的数值变化,增长的速率: ![20210810162216.png](./img/20210810162216.png) ![20210810164353.png](./img/20210810164353.png) ![20210810164656.png](./img/20210810164656.png) ![20210810164851.png](./img/20210810164851.png) 局部最小点较多--通过连接网络,达到极小值: ![20210810165220.png](./img/20210810165220.png) 马鞍形:一个方向为最小值,一个方向为最大值:鞍点 ![20210810165416.png](./img/20210810165416.png) 找到极值点的影响因素: ![20210810165848.png](./img/20210810165848.png) 初始状态: 何恺明,初始化状态: https://arxiv.org/pdf/1502.01852.pdf ![20210810170249.png](./img/20210810170249.png) 学习率 ![20210810171558.png](./img/20210810171558.png) 动速 如何逃出局部最小值: ![20210810171739.png](./img/20210810171739.png) 常见函数梯度: 常见函数: ![20210810171920.png](./img/20210810171920.png) 线性函数 y=wx+b---对w,b进行偏微分求解--》x+1 ![20210810172112.png](./img/20210810172112.png) ![20210810172304.png](./img/20210810172304.png) ![20210810172406.png](./img/20210810172406.png) ![20210810172628.png](./img/20210810172628.png) ![20210810172649.png](./img/20210810172649.png) ![20210810172752.png](./img/20210810172752.png) # 激活函数及梯度 ![20210810173113.png](./img/20210810173113.png) 这样的激活函数不可导 ![20210810173232.png](./img/20210810173232.png) 解决不可导,提出了这样的函数: sigmoid函数: ![20210811092323.png](./img/20210811092323.png) σ(sigmoid)的倒数是 σ'=σ*(1-σ) ![20210811092635.png](./img/20210811092635.png) 但在正负无穷的时候,σ' 趋近于0,导致导数迷散 ![20210811093040.png](./img/20210811093040.png) torch 中使用: from torch.nn import functional as F #函数接口 ## σ(sigmoid) a = torch.linspace(-100, 100, 10) print(a) #(sigmoid) sigmoid_σ = torch.sigmoid(a) print(sigmoid_σ) ![20210811093137.png](./img/20210811093137.png) tanh函数:在RNN中使用 ![20210811094340.png](./img/20210811094340.png) tanh的导数:x从正负无穷,y从-1,1 ![20210811094526.png](./img/20210811094526.png) 在torch中使用 ![20210811094710.png](./img/20210811094710.png) Rectified Linear Unit 整型的线性单元---ReLU ![20210811095032.png](./img/20210811095032.png) ![20210811095238.png](./img/20210811095238.png) torch中使用: ## ReLU函数 a=torch.linspace(-1, 1, 10) relu = torch.relu(a) print(relu) #tensor([0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.1111, 0.3333, 0.5556, 0.7778,1.0000]) ![20210811095401.png](./img/20210811095401.png) # LOSS及其梯度: ![20210811095735.png](./img/20210811095735.png) 经典loss ![20210811095944.png](./img/20210811095944.png) 均方差MSE: 有3种 ![20210811100247.png](./img/20210811100247.png) 求导: ![20210811101537.png](./img/20210811101537.png) torch自动求导: # 均方差MSE: 求导 x = torch.ones(1) w = torch.full([1], 2) #loss 值 mse = F.mse_loss(torch.ones(1), x * w) # torch.one(1)预测值,x*w计算值 w.requires_grad_() # 更新求导 #第1种方法 mse = F.mse_loss(torch.ones(1), x * w) # 重新计算 autograd = torch.autograd.grad(mse, [w]) print(autograd) # (tensor([2.]),) #第二种方法 mse = F.mse_loss(torch.ones(1), x * w) # 重新计算 mse.backward() # 向后传播 w_grad = w.grad print(w_grad) ![20210811102417.png](./img/20210811102417.png) 第2种方法 ![20210811103901.png](./img/20210811103901.png) ![20210811104553.png](./img/20210811104553.png) 分类问题--求出概率 softmax: soft version of max ![20210811105353.png](./img/20210811105353.png) i=j ![20210811105850.png](./img/20210811105850.png) i不等于j ![20210811110114.png](./img/20210811110114.png) ![20210811110411.png](./img/20210811110411.png) torch实现: # 分类问题--softmax函数 a = torch.rand(3) a.requires_grad_() # 更新求导 p = F.softmax(a, dim=0) # softmax 函数:预测值:a,dim对那个维度进行 autograd = torch.autograd.grad(p[1], [a], retain_graph=True)# p[1]:对第i个进行,[a]:变量 retain_graph声明求导 print(autograd) # (tensor([-0.0736, 0.1604, -0.0868]),) autograd = torch.autograd.grad(p[2], [a]) # retain_graph声明求导 print(autograd) # (tensor([-0.1586, -0.0868, 0.2455]),) ![20210811112302.png](./img/20210811112302.png) # 感知机: ![20210811114124.png](./img/20210811114124.png) 单层 感知机 模型: ![20210811114352.png](./img/20210811114352.png) ![20210811115449.png](./img/20210811115449.png) ![20210811120025.png](./img/20210811120025.png) ![20210811134442.png](./img/20210811134442.png) torch实现: # 单层 感知机 模型:实现 x = torch.randn(1, 10) w = torch.randn(1, 10, requires_grad=True) o = torch.sigmoid(x @ w.t()) # 激活函数 print(o) loss = F.mse_loss(torch.ones(1, 1), o) # loss函数 mse loss.backward() # 后向传播 w_grad = w.grad print(w_grad) # 得到w的导数 ![20210811135003.png](./img/20210811135003.png) 多层感知机:多层网络 ![20210811141808.png](./img/20210811141808.png) ![20210811142016.png](./img/20210811142016.png) torch实现: #多层 感知机 x = torch.randn(1, 10) w = torch.randn(2, 10, requires_grad=True) # 2层的 o = torch.sigmoid(x @ w.t()) # 激活函数 print(o) loss = F.mse_loss(torch.ones(1, 2), o) # loss函数 mse loss.backward() # 后向传播 w_grad = w.grad print(w_grad) # 得到w的导数 2x10 的导数 结果 ## 链式法则 链式法则是微积分中的求导法则,用以求一个复合函数的导数。 所谓的复合函数,是指以一个函数作为另一个函数的自变量。 如设 f(x)=3x,g(x)=x+3, g(f(x))就是一个复合函数, 并且g(f(x))=3x+3 链式法则(chain rule) 若h(x)=f(g(x)) 则h'(x)=f'(g(x))g'(x) 链式法则用文字描述, 就是“由两个函数凑起来的复合函数,其导数等于里边函数代入外边函数的值之导数,乘以里边函数的导数。 ![20210811164400.png](./img/20210811164400.png) ![20210811164649.png](./img/20210811164649.png) 各类rule原则: ![20210811164935.png](./img/20210811164935.png) ![20210811165112.png](./img/20210811165112.png) 乘积原则: ![20210811165228.png](./img/20210811165228.png) ![20210811165326.png](./img/20210811165326.png) ![20210811165637.png](./img/20210811165637.png) 链式法则过程: ![20210811165920.png](./img/20210811165920.png) torch实现: #链式法则 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])[0] dy1_dw1 = torch.autograd.grad(y1, [w1])[0] dy2_dw1 = dy2_dy1 * dy1_dw1 # 手动 链式求导 print(dy2_dw1) #dy2_dw1_n = torch.autograd.grad(y2, [w1])[0] # 直接使用torch提供的求导 #print(dy2_dw1_n) ## 多层网络 反向传播Multi-Layer Perceptron(MLP) : ![20210811172410.png](./img/20210811172410.png) ![20210811172605.png](./img/20210811172605.png) ![20210812085749.png](./img/20210812085749.png) #多层网络求导:当前层导数信息 乘以 后面层的导数信息之和。 ![20210811173052.png](./img/20210811173052.png) ![20210812090335.png](./img/20210812090335.png) ![20210812090540.png](./img/20210812090540.png) ![20210812091220.png](./img/20210812091220.png) ![20210812091614.png](./img/20210812091614.png) ## 2d 函数优化 ![20210812091838.png](./img/20210812091838.png) 示例: ![20210812091932.png](./img/20210812091932.png) ![20210812092113.png](./img/20210812092113.png) python 实现函数,绘制图形: ![20210812092301.png](./img/20210812092301.png) torch迭代,找到最小值: ![20210812093207.png](./img/20210812093207.png) import numpy as np from mpl_toolkits.mplot3d import Axes3D from matplotlib import pyplot as plt import torch #2d 函数 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() # plt显示 #[1., 0.], [-4, 0.], [4, 0.] x = torch.tensor([-4., 0.], requires_grad=True) # 初始化值 optimizer = torch.optim.Adam([x], lr=1e-3) # 使用torch内置的优化函数 for step in range(20000): # 迭代 pred = himmelblau(x) # 每次迭代值 optimizer.zero_grad() pred.backward() # 后向传播,求导 optimizer.step() # 迭代求导 if step % 2000 == 0: print(pred) print('step {}: x = {}, f(x) = {}' .format(step, x.tolist(), pred.item())) # 打印求导结果 ## 逻辑回归/回归分析 Logistic Regression ![20210812094141.png](./img/20210812094141.png) ![20210812094639.png](./img/20210812094639.png) ![20210812094742.png](./img/20210812094742.png) ![20210812095137.png](./img/20210812095137.png) 不使用最大准确率: ![20210812100650.png](./img/20210812100650.png) 为什么叫逻辑回归: ![20210812101007.png](./img/20210812101007.png) 2分类: ![20210812101055.png](./img/20210812101055.png) 多分类: ![20210812101157.png](./img/20210812101157.png) softmax引入,更好的找到概率大小的值: ![20210812101322.png](./img/20210812101322.png) ![20210812101354.png](./img/20210812101354.png) ## 经典LOSS: ![20210812101737.png](./img/20210812101737.png) # 交叉熵 Cross Entropy Loss ![20210812101632.png](./img/20210812101632.png) 熵:不确定性,惊喜度。 熵越高,惊喜度越低,越稳定;熵越低,越不稳定,惊喜度越高 ![20210812102052.png](./img/20210812102052.png) ![20210812102409.png](./img/20210812102409.png) 二分类的交叉熵: ![20210812103010.png](./img/20210812103010.png) ![20210812103315.png](./img/20210812103315.png) 示例: ![20210812103607.png](./img/20210812103607.png) 对于分类问题,使用交叉熵,不使用MSE均方差: ![20210812104037.png](./img/20210812104037.png) ![20210812104145.png](./img/20210812104145.png) torch 实现: ![20210812104458.png](./img/20210812104458.png) ## 交叉熵 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) ce1 = F.nll_loss(pred_log, torch.tensor([3])) print(ce1) ce2 = F.cross_entropy(logits, torch.tensor([3])) print(ce2) # 多分类问题 实战: ![20210812105124.png](./img/20210812105124.png) ![20210812105156.png](./img/20210812105156.png) torch 实现: 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) # 使用了relu 整型的线性单元函数,结果为0或者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) # torch 内置的迭代函数--优化器 https://www.jianshu.com/p/f8955dbc3553 criteon = nn.CrossEntropyLoss() # 与F.cross_entropy 相同 for epoch in range(epochs): # 数据训练 10次 for batch_idx, (data, target) in enumerate(train_loader): data = data.view(-1, 28 * 28) # 数据值 logits = forward(data) # 网络层函数 加载数据后,得到分类值 loss = criteon(logits, target) # 计算loss ,使用的是 交叉熵 值 optimizer.zero_grad() # 优化器 loss.backward() # 后向网络 #print(w1.grad.norm(), w2.grad.norm()) optimizer.step() # 优化器迭代 if batch_idx % 100 == 0: Train Epoch: 9[40000 / 60000(67 %)] Loss: 0.083235 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() # 预测值 与 测试值 是否eq相同,集合 test_loss /= len(test_loader.dataset) #平均loss,准确率 #Test set: Average loss: 0.0007, Accuracy: 9557 / 10000(95 %) print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format( test_loss, correct, len(test_loader.dataset),100. * correct / len(test_loader.dataset))) ## 激活函数与GPU加速 tanh函数,sigmoid函数 ![20210812113807.png](./img/20210812113807.png) RELU函数及其他形式: ![20210812113952.png](./img/20210812113952.png) LeakyReLU: ![20210812114638.png](./img/20210812114638.png) torch实现: F.leaky_relu() nn.LeakyReLU(inplace=True) ![20210812114726.png](./img/20210812114726.png) SERelu: ![20210812133544.png](./img/20210812133544.png) gup加速:1.0后,更好实现了 ![20210812134116.png](./img/20210812134116.png) ![20210812134713.png](./img/20210812134713.png) device = torch.device('cuda:0') #gpu 加速 只有一个cuda显卡直接写为0 net = MLP().to(device)#gpu 加速 optimizer = optim.SGD(net.parameters(), lr=learning_rate) criteon = nn.CrossEntropyLoss().to(device)#gpu 加速 ##test计算: 不断的训练,导致结果不发生改变了,使用其他数据test时,会出现后面效果很差 ![20210812135805.png](./img/20210812135805.png) argmax 找到最大值的索引,eq进行比较计算,准确率: ![20210812141740.png](./img/20210812141740.png) ![20210812142102.png](./img/20210812142102.png) When to test test once per several batch test once per epoch epoch V.S. step? 何时测试 每批次 测试一次 每个代 测试一次 ![20210812144425.png](./img/20210812144425.png) Epoch: 训练整个数据集的次数。 当一个完整的数据集通过了神经网络一次并且返回了一次,这个过程称为一次epoch。(也就是说,所有训练样本在神经网络中都进行了一次正向传播 和一次反向传播 ) 然而,当一个Epoch的样本(也就是所有的训练样本)数量可能太过庞大(对于计算机而言),就需要把它分成多个小块,也就是就是分成多个Batch 来进行训练。 Batch(批 / 一批样本): 将整个训练样本分成若干个Batch。 Batch_Size(批大小): 每批样本的大小。 Iteration(一次迭代): 训练一个Batch就是一次Iteration(这个概念跟程序语言中的迭代器相似)。 举例 mnist 数据集有60000张图片作为训练数据,10000张图片作为测试数据。假设现在选择 Batch_Size = 100对模型进行训练。迭代30000次。 每个 Epoch 要训练的图片数量:60000 (训练集上的所有图像) 训练集具有的 Batch 个数:60000/100=600 每个 Epoch 需要完成的 Batch 个数:600 每个 Epoch 具有的 Iteration 个数:600(==batch,训练一次batch相当于迭代一次) 每个 Epoch 中发生模型权重更新的次数:600 训练 10 个Epoch后,模型权重更新的次数:600*10=6000 不同Epoch的训练,其实用的是同一个训练集的数据。第1个Epoch和第10个Epoch虽然用的都是训练集的60000张图片,但是对模型的权重更新值却是完全不同的。因为不同Epoch的模型处于代价函数空间上的不同位置,模型的训练代越靠后,越接近谷底,其代价越小。 总共完成30000次迭代,相当于完成了30000/600 = 50 个Epoch 链接:https://www.jianshu.com/p/45b1c4d30dbe https://www.jianshu.com/p/22c50ded4cf7 ## Visdom可视化: pip install tensorboardX ![20210812144736.png](./img/20210812144736.png) TensorboardX ![20210812144914.png](./img/20210812144914.png) visdom: ![20210812145142.png](./img/20210812145142.png) 安装 pip install visdom 运行 python -m visdom.server ![20210812150056.png](./img/20210812150056.png) ![20210812150133.png](./img/20210812150133.png) 有问题可以通过源码,安装 ![20210812150153.png](./img/20210812150153.png) pip uninstall visdom cd 到下载目录 //pip的执行 可以git clone到本地后进入文件夹 pip install -e . 安装 pip会自动将包复制到site-packages ![20210812150310.png](./img/20210812150310.png) 单条曲线: ![20210812150918.png](./img/20210812150918.png) 多条曲线: ![20210812151739.png](./img/20210812151739.png) 图片和文本的显示: ![20210812152048.png](./img/20210812152048.png) ![20210812152144.png](./img/20210812152144.png) ## 过拟合和欠拟合: ![20210812152833.png](./img/20210812152833.png) ![20210812153106.png](./img/20210812153106.png) ![20210812153209.png](./img/20210812153209.png) ![20210812153356.png](./img/20210812153356.png) ![20210812153516.png](./img/20210812153516.png) ![20210812153640.png](./img/20210812153640.png) 模型能力:model capacity ![20210812153850.png](./img/20210812153850.png) ![20210812153925.png](./img/20210812153925.png) 欠拟合Underfitting: 的表现是 在训练时和测试时, acc准确率不高,loss也不好 ![20210812154253.png](./img/20210812154253.png) 过拟合:Overfitting 在训练时, acc准确率高,loss也好 但在测试时, acc准确率不高,loss也不好 所以泛化能力不好,一般性差 ![20210812154637.png](./img/20210812154637.png) ![20210812154754.png](./img/20210812154754.png) ![20210812154923.png](./img/20210812154923.png) ![20210812155643.png](./img/20210812155643.png) ## 交叉验证 K-fold cross-validation Train-Val-Test划分 固定划分: 训练集--验证集--测试集 ![20210812160130.png](./img/20210812160130.png) ![20210812160327.png](./img/20210812160327.png) K-fold cross-validation:k-重交叉验证 把 训练集--验证集合并,划分为k份,顺序使用k-1份训练,1份验证, ![20210812160806.png](./img/20210812160806.png) ## 如何消除过拟合: ![20210812163055.png](./img/20210812163055.png) ![20210812163242.png](./img/20210812163242.png) how to reduce overfitting More date :更多的数据集 Constraint model complexity :合适的模型能力 shallow regularization Dropout Data argumentation Early Stopping ![20210812163848.png](./img/20210812163848.png) 这里讲一下 regularization: 迫使参数的范数接近于0,减少模型复杂度。 https://blog.csdn.net/niuniu990/article/details/88567008 ![20210812164131.png](./img/20210812164131.png) regularization的类型: L1:1范数 L2:2范数 ![20210812164244.png](./img/20210812164244.png) ![20210812164852.png](./img/20210812164852.png) regularization_loss=0 for param in model.parameters(): regularization_loss+=torch.sum(torch.abs(param)) #先求绝对值,再求和 classify_loss=criteon(logits,target) loss=classify_loss+0.01*regularization_loss # L1 regularization optimizer.zero_grad() loss.backward() optimizer.step() ![20210812165025.png](./img/20210812165025.png) device=torch.device('cuda:0') net=MLP().to(device) optimizer=optim.SCD(net.parameters(), lr=learning_rate, weight_decay=0.01) # L2 weight_decay criteon=nn.CrossEntropyLoss().to(device) ## 动量 momentum 与 学习率衰减 learning rate decay: ![20210812165343.png](./img/20210812165343.png) 动量 momentum: ![20210812170544.png](./img/20210812170544.png) ![20210812170702.png](./img/20210812170702.png) torch 实现: ![20210812170848.png](./img/20210812170848.png) optimizer = optim.SGD(net.parameters(), lr=learning_rate, momentum=0.78) #直接指定momentum大小 学习率衰减 learning rate decay:设置一个动态的学习率 ![20210812172747.png](./img/20210812172747.png) ![20210812172915.png](./img/20210812172915.png) ![20210812173006.png](./img/20210812173006.png) 方案: 1. ![20210812173144.png](./img/20210812173144.png) ![20210812173220.png](./img/20210812173220.png) Early Stopping: 即在每一个epoch结束时(一个epoch即对所有训练数据的一轮遍历)计算 validation data的accuracy,当accuracy不再提高时,就停止训练。这是很自然的做法,因为accuracy不再提高了,训练下去也没用。另外,这样做还能防止overfitting(过拟合)。 那么,怎么样才算是validation accuracy不再提高呢?并不是说validation accuracy一降下来,它就是“不再提高”,因为可能经过这个epoch后,accuracy降低了,但是随后的epoch又让accuracy升上去了,所以不能根据一两次的连续降低就判断“不再提高”。正确的做法是,在训练的过程中,记录最佳的validation accuracy,当连续10次epoch(或者更多次)没达到最佳accuracy时,你可以认为“不再提高”,此时使用early stopping。这个策略就叫“ no-improvement-in-n”,n即epoch的次数,可以根据实际情况取10、20、30…. ![20210813093151.png](./img/20210813093151.png) ![20210813093744.png](./img/20210813093744.png) Dropout: ![20210813094404.png](./img/20210813094404.png) 当一个复杂的前馈神经网络被训练在小的数据集时,容易造成过拟合。为了防止过拟合,可以通过阻止特征检测器的共同作用来提高神经网络的性能。 Dropout说的简单一点就是:我们在前向传播的时候,让某个神经元的激活值以一定的概率p停止工作,这样可以使模型泛化性更强,因为它不会太依赖某些局部的特征,如图1所示 ![20210813094530.png](./img/20210813094530.png) ![20210813094734.png](./img/20210813094734.png) ![20210813094948.png](./img/20210813094948.png) 添加了dropout后training和test部分不一样,需要人为的区分。 ![20210813095208.png](./img/20210813095208.png) 可以在网络层中直接调用dropout。 network = Sequential([layers.Dense(256, activation='relu'), layers.Dropout(0.5), # 0.5 rate to drop layers.Dense(128, activation='relu'), layers.Dropout(0.5), # 0.5 rate to drop layers.Dense(64, activation='relu'), layers.Dense(32, activation='relu'), layers.Dense(10)]) for step, (x,y) in enumerate(db): with tf.GradientTape() as tape: [b, 28, 28] => [b, 784] x = tf.reshape(x, (-1, 28*28)) [b, 784] => [b, 10] out = network(x, training=True) #test out = network(x, training=False) 为什么要batch 训练: stochastic 随机的, 并不是真的随机,符合某一分布 deterministic 确定性 ![20210813095729.png](./img/20210813095729.png) ![20210813100152.png](./img/20210813100152.png) 为什么要batch 训练: 数据很大的时候,不可能整个数据集load到显存中,为了节省显存 stochastic gradient descent 不再是,求整个样本的loss对w的梯度,而是,求一个 batch 梯度 ![20210813100334.png](./img/20210813100334.png) ![20210813100356.png](./img/20210813100356.png) # 卷积: ### 什么是卷积: ![20210813100632.png](./img/20210813100632.png) 图片表示:黑白图片每张图的每个点表示了灰度值从0-255可以变换到0-1 ![20210813100804.png](./img/20210813100804.png) ![20210813100712.png](./img/20210813100712.png) 彩色图片三个通道: ![20210813100921.png](./img/20210813100921.png) 一层一般值的是权值w和输出是一层,三个隐藏层,一共是4层(一般input layer不算) 多少参数就是有多少条线: ![20210813101324.png](./img/20210813101324.png) 局部相关性 感受野,感受不是全局的是一次一次小块: ![20210813101500.png](./img/20210813101500.png) 由此启发,对网络进行局部,相关性: 卷积神经网络,卷积指的是局部相关性 ,并权值共享: 取出,一个图片的中的局部,比如28x28的图片,取3x3,只做局部相关的连接 ![20210813101833.png](./img/20210813101833.png) ![20210813102434.png](./img/20210813102434.png) ![20210813102604.png](./img/20210813102604.png) 卷积:滑动小窗口从28x28变成3x3参数大大降低 本来有784条全连接的权值现在变成9个与之位置相关的位置权值,位置太远不考虑 ![20210813102903.png](./img/20210813102903.png) 卷积层的权值数量会成倍减少, 但是可以考虑到全局信息只是移动的时候是用的同样的参数,同时考虑了全局的属性但是又考虑了位置相近的信息, 小窗口和大窗口做的是相乘再累加再的一个操作变成点,这叫卷积操作 ![20210813103241.png](./img/20210813103241.png) 卷积函数: ![20210813103516.png](./img/20210813103516.png) 图片/信号 的 卷积操作: G(x,h)=x*h x坐标移动,乘以h,最后得到G 锐化: ![20210813103744.png](./img/20210813103744.png) 模糊: ![20210813104625.png](./img/20210813104625.png) 边缘: ![20210813104650.png](./img/20210813104650.png) 卷积处理过程: ![20210813104911.png](./img/20210813104911.png) # 卷积神经网络: ### 卷积 [20210813104854.png](./img/20210813104854.png) ![20210813105532.png](./img/20210813105532.png) ![20210813111119.png](./img/20210813111119.png) 基本名称概念: ![20210813111716.png](./img/20210813111716.png) input的通道数是多少,kernel的通道数也要是多少 x[1,3,28,28] #一张图片 三个通道rgb 2828的大小 one k:[3,3,3] :第一个3是对应x的三个通道,后面是kernel的size33,如果input的channel是16 则kernel的channel也必须是16,必须和inut的channel是一一对应的 multi-k:[16,3,3,3] 16表示一共有16个kernel,可能是不同类型的kernel比如edge、blur等等,第一个3表示是输入图片的通道RGB bias[16]:每个kernel会带一个偏置,所以16个kernel生成维度16的bias out:[1,16,26,26] #1表示几张图片就是batch,16和kernel的个数是对应的,2626输出图片的大小,这取决于padding的大小也有可能是2828 kernel也成为filter weight ![20210813113210.png](./img/20210813113210.png) ![20210813113749.png](./img/20210813113749.png) 通过一系列的卷积操作,进行特征提取: ![20210813114034.png](./img/20210813114034.png) torch 中实现: 实现二维的卷积神经网络 layer=nn.Conv2d(1,3,kernel_size=3,stride=1,padding=0) #Conv2d:2d的函数卷积运算 1:一张图片 3:kernel的数量 kernel_size:kernel的大小,3x3 stride:移动的步长 padding:边缘补充的大小 #batch是1,3是ker个数,因为input_channel是3 #如果sride=2,就是隔一个看一个outputsize可能会折半,是一种降维的功能 ![20210813114951.png](./img/20210813114951.png) 推荐直接写:layer(x) w和b是需要梯度信息的在kernel卷积的过程中会自动更新 ![20210813115256.png](./img/20210813115256.png) 另一种写法: F.conv2d ![20210813115820.png](./img/20210813115820.png) ### 池化层:pooling--完成降维,的操作 pooling 下采样 把feature map变小,与图片缩小不太类似 down sample: ![20210813133554.png](./img/20210813133554.png) max pooling:每个卷积,取最大值, ![20210813133726.png](./img/20210813133726.png) arg pooling:平均取样 ![20210813133948.png](./img/20210813133948.png) ![20210813134122.png](./img/20210813134122.png) torch中的实现: import torch import numpy as np from torch.nn import functional as F # 函数接口 import torch.nn as nn # 函数接口 x = torch.randn(1, 16, 14, 14) layer = nn.MaxPool2d(2, stride=2) #n.MaxPool2d out = layer(x) print(out.shape) # torch.Size([1, 16, 7, 7]) out = F.avg_pool2d(x, 2, stride=2) #F.avg_pool2d print(out.shape) # torch.Size([1, 16, 7, 7]) ![20210813134354.png](./img/20210813134354.png) ### upsample:上采样--F.interpolate ![20210813132947.png](./img/20210813132947.png) ![20210813135524.png](./img/20210813135524.png) 实现: 上采样F.interpolate out = F.interpolate(x, scale_factor=2, mode='nearest') # 放大2倍,nearest就是采用临近复制的模式,channel通道不会改变 ![20210813135643.png](./img/20210813135643.png) ### ReLU:激活函数 一个简单的unit神经元单元一般是 conv2d- batch_normalization - pooling -relu relu函数加在feature_map上的效应: 把feature_map中负的单元给去掉,把相应低的点去掉,如图圈起来的相应太低,把该部分像素点变成0 ![20210813140050.png](./img/20210813140050.png) layer=nn.ReLu(inplace=True) #inplace=True 表示x->x’的过程,x’使用的是x的内存空间节省内存 之后会发现out最小值都变成0,因为负数都被过滤了 ![20210813140320.png](./img/20210813140320.png) layer=nn.ReLU(inplace=True) # inplace=True使用变量的内存空间,ReLU: out最小值都变成0,因为负数都被过滤了 out=layer(x) print(out.shape) # torch.Size([1, 16, 28, 28]) #print(x) # torch.Size([1, 16, 28, 28]) print(out) # torch.Size([1, 16, 28, 28]) 总的: import torch import numpy as np from torch.nn import functional as F # 函数接口 import torch.nn as nn # 函数接口 x = torch.randn(1, 16, 14, 14) layer = nn.MaxPool2d(2, stride=2) #n.MaxPool2d out = layer(x) print(out.shape) # torch.Size([1, 16, 7, 7]) out = F.avg_pool2d(x, 2, stride=2) #F.avg_pool2d print(out.shape) # torch.Size([1, 16, 7, 7]) out = F.interpolate(x, scale_factor=2, mode='nearest') # 放大2倍,nearest就是采用临近复制的模式,channel通道不会改变 print(out.shape) # torch.Size([1, 16, 28, 28]) layer=nn.ReLU(inplace=True) # inplace=True使用变量的内存空间,ReLU: out最小值都变成0,因为负数都被过滤了 out=layer(x) print(out.shape) # torch.Size([1, 16, 28, 28]) #print(x) # torch.Size([1, 16, 28, 28]) print(out) # torch.Size([1, 16, 28, 28]) ### BatchNorm: batch_normalization 批规范化-->批量正态分布 当正负无穷的时候,导数为趋近于0,神经网络学习很慢,https://blog.csdn.net/baidu_35231778/article/details/116157145 ![20210813141407.png](./img/20210813141407.png) 不是正态分布时候,不同起始点,找到最优解的路径不同: ![20210813142019.png](./img/20210813142019.png) 图片标准正态分布: ![20210813142625.png](./img/20210813142625.png) BatchNorm 批规范化-->批量正态分布: ![20210813143651.png](./img/20210813143651.png) ![20210813143956.png](./img/20210813143956.png) torch 实现: ![20210813144354.png](./img/20210813144354.png) ## BatchNorm 批规范化-->批量正态分布: x = torch.rand(100, 16, 784) layer = nn.BatchNorm1d(16) # BatchNorm out = layer(x) print(layer.running_mean) print(layer.running_var) 计算过程: ![20210813145028.png](./img/20210813145028.png) 2d实现: ![20210813145301.png](./img/20210813145301.png) ![20210813150529.png](./img/20210813150529.png) #二维直接使用.BatchNorm2d #因为Batch Norm的参数直接是由channel数量得来的, #因此这里直接给定了channel的数量为16,后续会输出16个channel的统计信息 layer = nn.BatchNorm2d(16) out = layer(x) #进行权值计算并输出 print(layer.weight) print(layer.bias) print(layer.running_mean) print(layer.running_var) test的时候,不使用,需要设置layer.evl() ![20210813150758.png](./img/20210813150758.png) layer.eval() # test的时候,不使用,需要设置layer.evl() #调用不同的模式,以完成参数是否自动更新学习 BatchNorm1d(16, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) 效果:往0,1靠近 收敛速度更快,效果好,更稳定,调整超参数,可以更大: 使用了Batch Norm后,收敛速度加快、精度提高。 上右图可看出尖峰的偏差对比左侧变小了很多。 ![20210813151218.png](./img/20210813151218.png) ![20210813151403.png](./img/20210813151403.png) ## 经典卷积神经网络:CNN 近期发展: https://www.bilibili.com/video/BV1U54y1E7i6?p=68 ![20210813151631.png](./img/20210813151631.png) LeNet-5: ![20210813152021.png](./img/20210813152021.png) AlexNet:使用了max pooling,relu,dropout ![20210813152506.png](./img/20210813152506.png) VGG:使用了更小的kennel:3x3,1x1.. ![20210813152821.png](./img/20210813152821.png) 1x1 Convolution: 通过1×1的卷积我们就可以对原始图片做一个变换,得到一张新的图片,从而可以提高泛化的能力减小过拟合,同时在这个过程中根据所选用的1×1卷积和filter的数目不同,可以实现跨通道的交互和信息的整合,而且还可以改变图片的维度.而且因为通过对维度的操作,虽然网络的层数增加了,但是网络的参数却可以大大减小,节省计算量。 ![20210813153026.png](./img/20210813153026.png) GoogLeNet ![20210813153725.png](./img/20210813153725.png) ![20210813153838.png](./img/20210813153838.png) 简单堆叠,不能得到好的效果: ![20210813154036.png](./img/20210813154036.png) ## ResNet:深度残差网络: ![20210813154243.png](./img/20210813154243.png) 残差网络 是由来自Microsoft Research的4位学者提出的卷积神经网络, 在2015年的ImageNet大规模视觉识别竞赛(ImageNet Large Scale Visual Recognition Challenge, ILSVRC)中 获得了图像分类和物体识别的优胜。 残差网络的特点是容易优化,并且能够通过增加相当的深度来提高准确率。 其内部的残差块使用了跳跃连接,缓解了 在深度神经网络中增加深度 带来的梯度消失问题 https://blog.csdn.net/qq_37554556/article/details/115057394 ![20210813154900.png](./img/20210813154900.png) ![20210813155145.png](./img/20210813155145.png) ![20210813155502.png](./img/20210813155502.png) 提升效果: ![20210813155619.png](./img/20210813155619.png) ![20210813160010.png](./img/20210813160010.png) 变种与发展: ![20210813160345.png](./img/20210813160345.png) resnet torch实现: ![20210813161030.png](./img/20210813161030.png) 代码后续写。。这个还是具体实现理解 DenseNet:每一层都与初始层相关,获取更多的信息 ![20210813161215.png](./img/20210813161215.png) ## nn.Module: 每一个层都继承了nn.Module:,可以使用求解每个层的模块 ![20210813161914.png](./img/20210813161914.png) ![20210813162414.png](./img/20210813162414.png) nn.Sequential: ![20210813162655.png](./img/20210813162655.png) nn.Parameter:参数设置: ![20210813164525.png](./img/20210813164525.png) 包含了所有节点 及 子 模块: ![20210813164958.png](./img/20210813164958.png) ![20210813165344.png](./img/20210813165344.png) .device:方便的, 选择gpu ![20210813165610.png](./img/20210813165610.png) load and save :方便的加载之前的训练状态,方便的保存当前训练状态 ![20210813165821.png](./img/20210813165821.png) net.load_state_dict(torch.load('ckpt.mdl')) torch.save(net.state_dict(), 'ckpt.mdl') tain,test 方便转换: ![20210813170132.png](./img/20210813170132.png) ```apache class TestNet(nn.Module): def __init__(self): super(TestNet, self).__init__() self.net = nn.Sequential(nn.Conv2d(1, 16, stride=1, padding=1), nn.MaxPool2d(2, 2), Flatten(),# 打平操作 nn.Linear(1 * 14 * 14, 10)) def forward(self, x): return self.net(x) class BasicNet(nn.Module): def __init__(self): super(BasicNet, self).__init__() self.net = nn.Linear(4, 3) def forward(self, x): return self.net(x) class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.net = nn.Sequential(BasicNet(), nn.ReLU(), nn.Linear(3, 2)) def forward(self, x): return self.net(x) def main(): device = torch.device('cuda') net = Net() net.to(device) net.train() net.eval() # net.load_state_dict(torch.load('ckpt.mdl')) # # # torch.save(net.state_dict(), 'ckpt.mdl') for name, t in net.named_parameters(): print('parameters:', name, t.shape) for name, m in net.named_children(): print('children:', name, m) for name, m in net.named_modules(): print('modules:', name, m) if __name__ == '__main__': main() ``` 自定义类: ```apache class MyLinear(nn.Module): #自定义类 def __init__(self, inp, outp): super(MyLinear, self).__init__() # requires_grad = True self.w = nn.Parameter(torch.randn(outp, inp)) #添加到Parameter中 self.b = nn.Parameter(torch.randn(outp)) def forward(self, x): x = x @ self.w.t() + self.b return x class Flatten(nn.Module): def __init__(self): super(Flatten, self).__init__() def forward(self, input): return input.view(input.size(0), -1) ``` ## 数据增强 Data argumentation Limited Data:有限数据下 Small network capacity:减小网络 Regularization:回归化,固化 transforms.Normalize Data argumentation:人为数据增强 ![20210813171855.png](./img/20210813171855.png) Data argumentation ▪ Flip:翻转 ![20210813172712.png](./img/20210813172712.png) ▪ Rotate:旋转 ▪ Random Move & Crop:随机移动和裁剪 ▪ GAN:生成和分析网络 # Cifar10 图片分类 -Lenet5实战 ![1629040944287.png](img/1629040944287.png) Lenet5:实现形式 ![1629079963937.png](img/1629079963937.png) 查看GPU 使用:gpustart ![1629095134184.png](img/1629095134184.png) nvidia-smi ![1629095154488.png](img/1629095154488.png) ![1629097301876.png](img/1629097301876.png) 代码见: ![1629098422144.png](img/1629098422144.png) # ResNet实战 ResNet block: ![1629098536832.png](img/1629098536832.png) resblk实现: ```apache mport torch from torch import nn from torch.nn import functional as F class ResBlk(nn.modules): # 新建类 res blk 单元 """ resnet block """ def __init__(self, ch_in, ch_out, stride=1): """ :param ch_in 输入 :param ch_out 输出 """ super(ResBlk, self).__init__() # we add stride support for resbok, which is distinct from tutorials. self.conv1 = nn.Conv2d(ch_in, ch_out, kernel_size=3, stride=stride, padding=1) # 先建立 卷积层 self.bn1 = nn.BatchNorm2d(ch_out) # 正态 规范化 self.conv2 = nn.Conv2d(ch_out, ch_out, kernel_size=3, stride=1, padding=1) # 第2个 卷积层 self.bn2 = nn.BatchNorm2d(ch_out) # 正态 规范化 self.extra = nn.Sequential() # 先建立 一个空的Sequential if ch_out != ch_in: # 输入 与输出 不同 # [b, ch_in, h, w] => [b, ch_out, h, w] self.extra = nn.Sequential( nn.Conv2d(ch_in, ch_out, kernel_size=1, stride=stride), # 在做卷积 变换 nn.BatchNorm2d(ch_out) ) def forward(self, x): """ :param x: [b, ch, h, w] :return: """ out = F.relu(self.bn1(self.conv1(x))) #Relu 激活函数,输出都为正的 out = self.bn2(self.conv2(out)) # short cut.--那个 额外的分支 # extra module: [b, ch_in, h, w] => [b, ch_out, h, w] # element-wise add: out = self.extra(x) + out out = F.relu(out) # 经过激活函数后,最后卷积层 输出的结果 return out ``` # 时间序列表示: ![1629106130714.png](img/1629106130714.png) ![1629106210517.png](img/1629106210517.png) 语音,文字的信号,如何在神经网络中表示: 语言按信号的波峰值,文字的字符--》word to vector ![1629160381289.png](img/1629160381289.png) ![1629160563175.png](img/1629160563175.png) 时间序列: ``` 对应2维度的,可以使用:[时间尺度,价格],进行维度表示 ``` ![1629160686245.png](img/1629160686245.png) 分时出现数据: ![1629160941329.png](img/1629160941329.png) 语言文字的表达方式: ![1629161029829.png](img/1629161029829.png) 语义相关性的两种方法: ![1629161217277.png](img/1629161217277.png) ![1629161530079.png](img/1629161530079.png) 查表操作,word2vec vs GloVe,现有的,这两种 ![1629161649822.png](img/1629161649822.png) ![1629161796656.png](img/1629161796656.png) # RNN:循环神经网络 ## 原理: w值 共享: ![1629162128311.png](img/1629162128311.png) 添加h(保留上一个的语境信息): ![1629162293770.png](img/1629162293770.png) ![1629162396868.png](img/1629162396868.png) ![1629162445645.png](img/1629162445645.png) ![1629162557715.png](img/1629162557715.png) ![1629162756572.png](img/1629162756572.png) ![1629162901870.png](img/1629162901870.png) ![1629163037971.png](img/1629163037971.png) ![1629163115950.png](img/1629163115950.png) ## RNN layer使用: ![1629163789946.png](img/1629163789946.png) nn.RNN 类的使用: RNN:[5,3,100]--->5:5个单词,3句话,100:每个单词用100个向量来表达 **nn.RNN(每个单词用的向量数/维度,memary/h的维度)** ![1629163824078.png](img/1629163824078.png) h:num layers层数,b:多少条曲线,h维度 ![1629164239375.png](img/1629164239375.png) ### 单层的RNN: ![1629164663156.png](img/1629164663156.png) ### 多层的RNN: h:最后一个时间点上的状态 out:所有时间点上的状态,多层网络这个不变 ![1629164839055.png](img/1629164839055.png) 2层: ![1629165190829.png](img/1629165190829.png) 4层: ![1629165270474.png](img/1629165270474.png) ### nn.RNNCell: RNN 单元 ![1629166132229.png](img/1629166132229.png) ![1629166275148.png](img/1629166275148.png) ![1629166378624.png](img/1629166378624.png) 2层的RNN Cell: ![1629166446399.png](img/1629166446399.png) ## RNN时间序列预测 实战: 曲线预测: ![1629166644660.png](img/1629166644660.png) ![1629167021476.png](img/1629167021476.png) 网络: ![1629167987498.png](img/1629167987498.png) ![1629169292527.png](img/1629169292527.png) ![1629169371665.png](img/1629169371665.png) ![1629169701879.png](img/1629169701879.png) ## RNN难题:梯度弥散,梯度爆炸: ![1629170383391.png](img/1629170383391.png) ![1629170496101.png](img/1629170496101.png) 梯度爆炸---解决方法: 对w的grad进行clipping: ![1629170752922.png](img/1629170752922.png) Clipping的实现: ![1629170900447.png](img/1629170900447.png) ```apache loss = criterion(output, y) # 预测值和真实值计算均方差 model.zero_grad() # 梯度清0 loss.backward() for p in model.parameters(): print(p.grad.norm()) # 打印梯度的模长 torch.nn.utils.clip_grad_norm_(p, 10) # 限制模长在10内 optimizer.step() ``` 梯度弥散---解决方法:使用LSTM 进行 ![1629171396081.png](img/1629171396081.png) ![1629171477790.png](img/1629171477790.png) # LSTM:Long Short-Term Memory ## 原理: 长短期记忆网络(LSTM,Long Short-Term Memory)是一种时间循环神经网络, 是为了解决一般的RNN(循环神经网络)存在的长期依赖问题而专门设计出来的。 ![1629171683253.png](img/1629171683253.png) ![1629171915902.png](img/1629171915902.png) 通过几个阀门控制: ![1629172008271.png](img/1629172008271.png) 符号--相乘x,相加+: ![1629172092233.png](img/1629172092233.png) 1--forget gate忘记门: ![1629172202426.png](img/1629172202426.png) 2--输入门,cell状态: ![1629172361409.png](img/1629172361409.png) 3--Ct: ![1629172437298.png](img/1629172437298.png) ht:输出 ![1629172564943.png](img/1629172564943.png) ![1629172661647.png](img/1629172661647.png) ![1629178098791.png](img/1629178098791.png) ![1629178212508.png](img/1629178212508.png) ![1629178360430.png](img/1629178360430.png) ## nn.LTSM使用: nn.LSTM(x的维度,c/h的维度,几层网络) ![1629178521059.png](img/1629178521059.png) ![1629178668943.png](img/1629178668943.png) ```apache import numpy as np import torch import torch.nn as nn lstm = nn.LSTM(input_size=100, hidden_size=20, num_layers=4) x = torch.randn(10, 3, 100) # 10个单词,3个句子,100:每个单词有100维度 out, (h, c) = lstm(x) #lstm(x,[h0, c0]),[h0, c0]默认使用0,可以忽略不添加 print(out.shape, h.shape, c.shape) # torch.Size([10, 3, 20]) torch.Size([4, 3, 20]) torch.Size([4, 3, 20]) ``` nn.LSTMCell: ![1629179262931.png](img/1629179262931.png) ![1629179459396.png](img/1629179459396.png) 1层: ![1629179543050.png](img/1629179543050.png) 2层: ![1629179716941.png](img/1629179716941.png) ## 情感分类问题--需要使用google colab进行,速度快,下载文件也快:--后续再试试吧: ![1629180626211.png](img/1629180626211.png) ![1629180640209.png](img/1629180640209.png) 数据集: torch text --->IMDB text-->x label-->y ![1629181533473.png](img/1629181533473.png) ![1629182983125.png](img/1629182983125.png) ![1629183002115.png](img/1629183002115.png) ![1629183082332.png](img/1629183082332.png) ![1629183693218.png](img/1629183693218.png) 需要使用google colab进行,速度快,下载文件也快:--后续再试试吧: ```apache # -*- coding: utf-8 -*- """lstm Automatically generated by Colaboratory. Original file is located at https://colab.research.google.com/drive/1GX0Rqur8T45MSYhLU9MYWAbycfLH4-Fu """ # !pip install torch # !pip install torchtext # !python -m spacy download en # K80 gpu for 12 hours import torch from torch import nn, optim from torchtext import data, datasets print('GPU:', torch.cuda.is_available()) torch.manual_seed(123) TEXT = data.Field(tokenize='spacy') LABEL = data.LabelField(dtype=torch.float) train_data, test_data = datasets.IMDB.splits(TEXT, LABEL) # torch text print('len of train data:', len(train_data)) print('len of test data:', len(test_data)) print(train_data.examples[15].text) # x print(train_data.examples[15].label) # y # word2vec, glove TEXT.build_vocab(train_data, max_size=10000, vectors='glove.6B.100d') LABEL.build_vocab(train_data) batchsz = 30 device = torch.device('cuda') train_iterator, test_iterator = data.BucketIterator.splits( (train_data, test_data), batch_size=batchsz, device=device ) class RNN(nn.Module): def __init__(self, vocab_size, embedding_dim, hidden_dim): """ """ super(RNN, self).__init__() # [0-10001] => [100] self.embedding = nn.Embedding(vocab_size, embedding_dim) # vocab_size:10000,embedding_dim:100 # [100] => [256] self.rnn = nn.LSTM(embedding_dim, hidden_dim, num_layers=2, bidirectional=True, dropout=0.5) # bidirectional是否双向传播,默认值为False,dropout:防止过拟合 # [256*2] => [1] self.fc = nn.Linear(hidden_dim * 2, 1) # 把h的信息转为1维的 self.dropout = nn.Dropout(0.5) def forward(self, x): """ x: [seq_len, b] vs [b, 3, 28, 28] """ # [seq, b, 1] => [seq, b, 100] embedding = self.dropout(self.embedding(x)) # embedding编码--dropout减少过拟合增强鲁棒性 # output: [seq, b, hid_dim*2] # hidden/h: [num_layers*2, b, hid_dim] #因为是双向的,所以*2 # cell/c: [num_layers*2, b, hid_di] output, (hidden, cell) = self.rnn(embedding) # [num_layers*2, b, hid_dim] => 2 of [b, hid_dim] => [b, hid_dim*2] hidden = torch.cat([hidden[-2], hidden[-1]], dim=1) # 对hidden cat 截取出-> b, hid_dim # [b, hid_dim*2] => [b, 1] hidden = self.dropout(hidden) # dropout减少过拟合增强鲁棒性 out = self.fc(hidden) # 把h的信息转为1维的 [0.3,0.7,0.1...] return out rnn = RNN(len(TEXT.vocab), 100, 256) # load word embedding 将每个词语训练成,词向量 pretrained_embedding = TEXT.vocab.vectors # 下载glove后,进行权值 赋值 print('pretrained_embedding:', pretrained_embedding.shape) rnn.embedding.weight.data.copy_(pretrained_embedding) # 用glove后权值 赋值 print('embedding layer inited.') optimizer = optim.Adam(rnn.parameters(), lr=1e-3) criteon = nn.BCEWithLogitsLoss().to(device) rnn.to(device) import numpy as np def binary_acc(preds, y): """ get accuracy """ preds = torch.round(torch.sigmoid(preds)) correct = torch.eq(preds, y).float() acc = correct.sum() / len(correct) return acc # 训练 def train(rnn, iterator, optimizer, criteon): avg_acc = [] rnn.train() for i, batch in enumerate(iterator): # [seq, b] => [b, 1] => [b] pred = rnn(batch.text).squeeze(1) # rnn计算后的预测值 # loss = criteon(pred, batch.label) # 计算loss acc = binary_acc(pred, batch.label).item() # 准确率计算 avg_acc.append(acc) # 平均准确率 optimizer.zero_grad() loss.backward() optimizer.step() # 迭代 if i % 10 == 0: print(i, acc) avg_acc = np.array(avg_acc).mean() print('avg acc:', avg_acc) # 平均准确率 # test def eval(rnn, iterator, criteon): avg_acc = [] rnn.eval() with torch.no_grad(): for batch in iterator: # [b, 1] => [b] pred = rnn(batch.text).squeeze(1) # 预测值 # loss = criteon(pred, batch.label) # loss acc = binary_acc(pred, batch.label).item() avg_acc.append(acc) avg_acc = np.array(avg_acc).mean() print('>>test:', avg_acc) # 平均准确率 for epoch in range(10): # 运行计算迭代 eval(rnn, test_iterator, criteon) train(rnn, train_iterator, optimizer, criteon) ``` # 自定义数据集实战: ![1629183707730.png](img/1629183707730.png) 数据下载地址: ▪ 链接: https://pan.baidu.com/s/1V_ZJ7ufjUUFZwD2NHSNMFw ▪ 提取码:dsxl ![1629185246163.png](img/1629185246163.png) ![1629185477710.png](img/1629185477710.png) ![1629185592909.png](img/1629185592909.png) ![1629185715700.png](img/1629185715700.png) ![1629185814034.png](img/1629185814034.png) ## 自定义数据集: ![1629186157292.png](img/1629186157292.png) ```#apache from torch.utils.data import Dataset, DataLoader class NumbersDataset(Dataset): # 自定义 数据集 def __init__(self, tranining=True): # tranining=True,是否作为训练集 if tranining: self.samples = list(range(1, 1001)) # 训练集 else: self.samples = list(range(1001, 1501)) # 测试集 def len_(self): return len(self.sampLes) # 数据长度 def __getitem__(self, idx): return self.samples[idx] # 返回 指定长度idx的数据 ``` ## 数据预处理: 因为net输入的图片都是固定长度的,所以需要,预处理图片数据: im ▪ Image Resize--图片尺寸固定 ``` 224x224 for ResNet18 ``` ▪ Data Argumentation--数据增强 ``` Rotate ``` ``` Crop ``` ▪ Normalize--数据标准化/数据归一化 ``` Mean, std ``` ▪ ToTensor--转换为tensor ![1629186927502.png](img/1629186927502.png) ![1629192737592.png](img/1629192737592.png) ![1629247923467.png](img/1629247923467.png) 旋转后,有黑边, ```apache transforms.CenterCrop(self.resize), #旋转后,做中心裁剪,避免黑边 ``` ``` 还原标准化后的图片 , 标准化后图片 ``` ![1629249614002.png](img/1629249614002.png) ```apache def denormalize(self, x_hat): #还原标准差后的 图片 mean = [0.485, 0.456, 0.406] std = [0.229, 0.224, 0.225] # x_hat = (x-mean)/std # x = x_hat*std = mean # x: [c, h, w] # mean: [3] => [3, 1, 1] mean = torch.tensor(mean).unsqueeze(1).unsqueeze(1) # 增加维度,由[3] => [3, 1, 1] std = torch.tensor(std).unsqueeze(1).unsqueeze(1) # print(mean.shape, std.shape) x = x_hat * std + mean # 还原标准差后 值 return x ``` ## 构建网络 ![1629251979603.png](img/1629251979603.png) 通过 对 resnet.py 修改:来使用 ```apache import torch from torch import nn from torch.nn import functional as F class ResBlk(nn.Module): # 新建类 res blk 单元 nn.module,这个module的m要大写M """ resnet block """ def __init__(self, ch_in, ch_out, stride=1): """ :param ch_in 输入 :param ch_out 输出 变换后的矩阵维度 """ super(ResBlk, self).__init__() # we add stride support for resbok, which is distinct from tutorials. self.conv1 = nn.Conv2d(ch_in, ch_out, kernel_size=3, stride=stride, padding=1) # 先建立 卷积层,设置stride减少数据量, self.bn1 = nn.BatchNorm2d(ch_out) # 正态 规范化 self.conv2 = nn.Conv2d(ch_out, ch_out, kernel_size=3, stride=1, padding=1) # 第2个 卷积层 self.bn2 = nn.BatchNorm2d(ch_out) # 正态 规范化 self.extra = nn.Sequential() # 先建立 一个空的Sequential if ch_out != ch_in: # 输入 与输出 不同 # [b, ch_in, h, w] => [b, ch_out, h, w] self.extra = nn.Sequential( nn.Conv2d(ch_in, ch_out, kernel_size=1, stride=stride), # 在做卷积 变换,设置stride减少数据量, nn.BatchNorm2d(ch_out) ) def forward(self, x): """ :param x: [b, ch, h, w] :return: """ out = F.relu(self.bn1(self.conv1(x))) # Relu 激活函数,输出都为正的 out = self.bn2(self.conv2(out)) # short cut.--那个 额外的分支 # extra module: [b, ch_in, h, w] => [b, ch_out, h, w] # element-wise add: out = self.extra(x) + out out = F.relu(out) # 经过激活函数后,最后卷积层 输出的结果 return out # ResNet18 class ResNet18(nn.Module): def __init__(self, num_class): #num_class:标签分类 数量 super(ResNet18, self).__init__() self.conv1 = nn.Sequential( nn.Conv2d(3, 16, kernel_size=3, stride=3, padding=0), # 卷积层--作为预处理,3:输入3,16:输出16 nn.BatchNorm2d(16) ) # followed 4 blocks 4个块 # [b, 64, h, w] => [b, 128, h ,w] self.blk1 = ResBlk(16, 32, stride=3) # 使用自建的计算 单元,stride=2,每次步长后,图片长宽都会减小 # [b, 128, h, w] => [b, 256, h, w] self.blk2 = ResBlk(32, 64, stride=3) # # [b, 256, h, w] => [b, 512, h, w] self.blk3 = ResBlk(64, 128, stride=2) # # [b, 512, h, w] => [b, 1024, h, w] # self.blk4 = ResBlk(512, 1024, stride=2) self.blk4 = ResBlk(128, 256, stride=2) # [b, 256, 3,3]==>[256 * 3 * 3,num_class] self.outlayer = nn.Linear(256 * 3 * 3, num_class) # 输出层:nn.Linear建立线性的层,# num_class:标签分类 数量 def forward(self, x): # 建立向前计算的 网络 """ :param x: :return: """ x = F.relu(self.conv1(x)) # 第一个 卷积层 # [b, 64, h, w] => [b, 1024, h, w] 4个块/计算块 x = self.blk1(x) x = self.blk2(x) x = self.blk3(x) x = self.blk4(x) #print('after conv:', x.shape) # [b, 512, 2, 2],after conv: torch.Size([2, 512, 2, 2]) #x = F.adaptive_avg_pool2d(x, [1, 1]) # 把[b, 512, h, w] 变为=> [b, 512, 1, 1],[1, 1]为图片尺寸 x = x.view(x.size(0), -1) # 拉伸为一行(batch_size, 512 * 1 * 1),网络 打平 为512 * 1 * 1 x = self.outlayer(x) # 输入 (batch_size, 512 * 1 * 1),输出:[2, 10] return x def main(): # Res块的测试 # blk = ResBlk(64, 128, stride=2) # block: torch.Size([2, 128, 16, 16]),32/2 blk = ResBlk(64, 128) # block: torch.Size([2, 128, 16, 16]),32/2 tmp = torch.randn(2, 64, 224, 224) out = blk(tmp) print('block:', out.shape) #torch.Size([2, 128, 224, 224]) # 输入res net 测试 model = ResNet18(5) # 5:5个标签 5类 x = torch.randn(2, 3, 224, 224) out = model(x) print('resnet:', out.shape) # torch.Size([2, 5]) #model.parameters():模型的参数,p.numel():参数的大小 p = sum(map(lambda p:p.numel(), model.parameters())) print('parameters size:', p)#输出参数的 大小 parameters size: 1234885 if __name__ == '__main__': main() ``` ## 训练和测试: ![1629253315936.png](img/1629253315936.png) 数据1千张,有还是比较少,最好是: ![1629256920818.png](img/1629256920818.png) ![1629256983074.png](img/1629256983074.png) ```apache # 模型 训练 import torch from torch import optim, nn import visdom import torchvision from torch.utils.data import DataLoader from pokemon import Pokemon from my_resnet import ResNet18 batchsz = 32 # 数据批次 32张 lr = 1e-3 # 学习率 epochs = 10 # 整个数据次数 10次 device = torch.device('cuda') # 使用gpu torch.manual_seed(1234) # 确保每次运行.py文件时,生成的随机数都是固定的,这样每次实验结果显示也就一致了https://www.cnblogs.com/dychen/p/13920000.html # 数据加载 train_db = Pokemon('pokemon', 224, mode='train') # 训练集 val_db = Pokemon('pokemon', 224, mode='val') # 验证集 test_db = Pokemon('pokemon', 224, mode='test') # 训练集 train_loader = DataLoader(test_db, batch_size=batchsz, shuffle=True, num_workers=4) # num_workers:从RAM中找本轮迭代要用的batch 用batch_sampler将指定batch分配给指定worker https://blog.csdn.net/qq_24407657/article/details/103992170 val_loader = DataLoader(val_db, batch_size=batchsz, num_workers=2) # shuffle=False 不用随机了 test_loader = DataLoader(test_db, batch_size=batchsz, num_workers=2) viz = visdom.Visdom() #可视化 def evalute(model, loader): # 计算总的准确率,评估函数 model.eval() correct = 0 total = len(loader.dataset) for x, y in loader: x, y = x.to(device), y.to(device) with torch.no_grad(): # 不计算梯度 logits = model(x) pred = logits.argmax(dim=1) # 预测值 correct += torch.eq(pred, y).sum().float().item() # 累加 相等次数 return correct / total # 计算总的准确率 def main(): model = ResNet18(5).to(device) # 创建模型,5:分类,.to(device):使用gpu optimizer = optim.Adam(model.parameters(), lr=lr) # Adam优化器,一般使用整个优化器 criteon = nn.CrossEntropyLoss() # loss 使用交叉熵loss best_acc=0 best_epoch =0 # 最高的准确率,最好的迭代次 global_step = 0 #visdom x的坐标 viz.line([0], [-1], win='loss', opts=dict(title='loss')) #初始化visdom中的一条线线 viz.line([0], [-1], win='val_acc', opts=dict(title='val_acc')) #初始化visdom中的一条线线 for epoch in range(epochs): for step, (x, y) in enumerate(train_loader): # enumerate同时列出数据和数据下标 # x: [b, 3, 224, 224], y: [b] x, y = x.to(device), y.to(device) # gpu使用 model.train() logits = model(x) # 模型数据加载 loss = criteon(logits, y) # 计算预测值,与y的 loss optimizer.zero_grad() loss.backward() optimizer.step() # 优化器 迭代 # 可视化 viz.line([loss.item()], [global_step], win='loss', update='append') #更新loss曲线 global_step += 1 #更新 visdom viz.line x的坐标 if epoch % 2 == 0: val_acc = evalute(model, val_loader) ## 计算总的准确率 if val_acc > best_acc: best_acc = val_acc best_epoch=epoch torch.save(model.state_dict(), 'best.mdl') # 保存最高准确率时,模型状态参数 #可视化 viz.line([val_acc], [global_step], win='val_acc', update='append') #更新准确率 print('best acc:', best_acc, 'best epoch:', best_epoch) #打印 最高准确率 model.load_state_dict(torch.load('best.mdl')) #加载 保存的最优模型参数 print('loaded from ckpt!') test_acc = evalute(model, test_loader) #使用测试集 测试准确率值 print('test acc:', test_acc) if __name__ == '__main__': main() ``` ## 提升分类准确率: ### **Transfer learning**:迁移学习 利用分类好的模型,添加现有的图片,进行微调,得到符合现在的分类器: ![1629257386464.png](img/1629257386464.png) ![1629257386464.png](img/1629257661454.png) 实现: ```apache from torchvision.models import resnet18 #从torch中加载训练好,结构化的模型 ``` 会下载到C盘: ``` C:\Users\Administrator/.torch\models\resnet18-5c106cde.pth ``` ![1629262151434.png](img/1629262151434.png) ```apache trained_model = resnet18(pretrained=True) # 使用 训练好的模型 model = nn.Sequential( # list(trained_model.children())[:-1]:取出除去最后一个的前面模型 # *是打散模型 *list(trained_model.children())[:-1], # 模型的维度:[b, 512, 1, 1] Flatten(), # [b, 512, 1, 1] => [b, 512*1*1] 把后面的打平 nn.Linear(512, 5) # 再做最后一层,变为[512,5] ).to(device) ``` ![1629266316405.png](img/1629266316405.png) **总结**: 数据预处理, 数据加载, 搭建网络, 训练及测试, 改进(这里使用的是迁移学习,使用训练过的网络与现有数据集合) # Auto-Encoders:无监督学习/自编码器 ## 原理 ![1629266389414.png](img/1629266389414.png) 无标签数据: ![1629266456835.png](img/1629266456835.png) ![1629266613344.png](img/1629266613344.png) 通过对无标签数据进行学习,得到数据的信息: 降低维度,可视化,提取有用的数据,其他 ![1629266852385.png](img/1629266852385.png) **无监督学习** 无监督学习常见的两种类型是: 数据集变换 和 聚类。 **数据集变换** ,就是创建数据集新的表示算法,与数据的原始原始表示相比,新的表示可能更容易被人或其他机器学习算法所理解。 常见的应用有降维,就是对于许多特征表示的高维数据,找到表示该数据的一种新方法,用较少的特征就可以概括其重要特性。另一个应用就是找到“构成”数据的各个组成部分,比如对文本文档的关键字提取。 **聚类,** 就是将数据划分成不同的组,每组包含相似的物项。 在无监督学习中, Auto-Encoders的目标是重建自己, 它是一个特殊的全连接层,输入和输出的维度是一样的,这样能保证自己能够重建。 中间有一个neck(脖子),这样既可以升维也可以降维,这里降到两维的好处是, 二维的图片是可视化的,不仅已经降维,而且在空间中还保证了语义的相关性(通过无监督的聚类可以发现)。 ![1629267182430.png](img/1629267182430.png) ①交叉熵, ②MSE ![1629267627295.png](img/1629267627295.png) **PCA降维和Auto-Encoders降维的比较** PCA在高维数据中寻找方差最大的方向,只选择方差最大的轴。然而,PCA具有线性特性,这对特征维数的提取有很大的限制。 ![1629267703442.png](img/1629267703442.png) ![1629267862822.png](img/1629267862822.png) Auto-Encoders比PCA降维的效果要好: ![1629267985272.png](img/1629267985272.png) 变种: Denoising AutoEncoders,去噪 AutoEncoders 如果只在像素级别的重建,便不能发现一些更加深层次的特征,网路可能会记住一些特征,为了防止这种情况出现,我们可以在原输入图片后加入随机噪声累加到原图片上: ![1629268094282.png](img/1629268094282.png) Dropout AutoEncoders 在训练的时候随机对某些连接进行断开(通过将该连接的w设置为0),那么将会迫使网络尽可能的提升还存在的连接的表征能力,降低对多个神经元的依赖程度。 绿色图的x是训练时loss的Dropout率,y是loss,当为1时表示全部断开,loss最大, 然而当dropout率为0时,右边蓝色图的y(test的acc)并不是最大, 说明ropout率为0.2时在一定程度生可以缓解过拟合现象。 ![1629268293014.png](img/1629268293014.png) 对抗 AutoEncoders: ![1629268594300.png](img/1629268594300.png) 在原始的AutoEncoders中,没有呈现出原有数据的分布,有可能生成的数据是一样的,Adversarial AutoEncoders额外的添加了一个Discriminator(鉴别器), 我们希望生成的Z符合真实的Z**^‘^** 的分布, 将真实的和生成的都送到鉴别器计算差距,如果属于希望的分布就输出为1,否则输出为0. ![1629268702760.png](img/1629268702760.png) 实现方法:q分布和p分布 ①前部分相当于欧氏距离,θ和o是两个网络,logp~o~ (x~i~ /z)表示在o网络下,给定一个z得到一个输出x~i~ 的概率 ②KL散度:KL(P||Q)= ∫~∞ ~ p(x)log(p(x)/q(x))dx ,P和Q是两个分布,p(x)和q(x)是x分别属于这两个分布的概率,KL散度衡量了两个分布之间的距离,当两个分布的重叠区域越小时,KL散度也就越小。 ![1629269255756.png](img/1629269255756.png) KL散度的示意: ![1629269361913.png](img/1629269361913.png) 最大相近度: ![1629269465754.png](img/1629269465754.png) ![1629269575977.png](img/1629269575977.png) KL的计算: ![1629269314097.png](img/1629269314097.png) ![1629335495839.png](img/1629335495839.png) ③真实数据x,通过隐藏层h,重构为x^’^ 因为通过h后,只是得到一个分布,因此需要进行sample()采样,而sample()是不可微的,将z变成z=u + nuo * eta,就能解决,而eta不可导,但是eta是不需要更新的,所以no care。 ![1629270160097.png](img/1629270160097.png) 其中再参数化技巧: ![1629270239669.png](img/1629270239669.png) 实现形式: ```apache # [b, 20], including mean and sigma h_ = self.encoder(x) # [b, 20] => [b, 10]均值m/μ 的维度 and [b, 10]方差σ 的维度,h_.chunk做维度拆分 mu, sigma = h_.chunk(2, dim=1) #μ,σ # reparametrize trick重新参数化, epison~N(0, 1),torch.randn_like返回再(0,1)上均匀分布的随机数 h = mu + sigma * torch.randn_like(sigma) ``` 添加KL后, 变换增强了: ![1629270402510.png](img/1629270402510.png) 得到了--变分自编码器 变分自编码器: ![1629270631560.png](img/1629270631560.png) 效果对比: ![1629270730136.png](img/1629270730136.png) 生成模型,得知q的分布,可以对原有图片进行重新添加q分布后,生成新的图片: ![1629270879196.png](img/1629270879196.png) ![1629272054786.png](img/1629272054786.png) ## Auto-Encoders实践: ![1629278761370.png](img/1629278761370.png) 数据: 使用本地的mnist: '../../data':下的processed,raw: ```apache # 使用本地的 mnist:processed,raw mnist_train = datasets.MNIST('../../data', train=True, transform=transforms.Compose([ transforms.ToTensor() ]), download=False) mnist_test = datasets.MNIST('../../data', train=False, transform=transforms.Compose([ transforms.ToTensor() ]), download=False) mnist_train = DataLoader(mnist_train, batch_size=32, shuffle=True) mnist_test = DataLoader(mnist_test, batch_size=32, shuffle=True) x, _ = iter(mnist_train).next() print('x:', x.shape) ``` ae.py: ```apache import torch from torch import nn class AE(nn.Module): def __init__(self): super(AE, self).__init__() # [b, 784] => [b, 20] self.encoder = nn.Sequential( nn.Linear(784, 256), nn.ReLU(), nn.Linear(256, 64), nn.ReLU(), nn.Linear(64, 20), nn.ReLU() ) # [b, 20] => [b, 784] self.decoder = nn.Sequential( nn.Linear(20, 64), nn.ReLU(), nn.Linear(64, 256), nn.ReLU(), nn.Linear(256, 784), nn.Sigmoid() # 变为0或1的值 ) def forward(self, x): """ :param x: [b, 1, 28, 28] :return: """ batchsz = x.size(0) # 数据输入,指输入第一个 # flatten x = x.view(batchsz, 784) # 打平 # encoder x = self.encoder(x) # decoder x = self.decoder(x) # reshape x = x.view(batchsz, 1, 28, 28) # 恢复数据维度 return x, None ``` main.py ```apache import torch from torch.utils.data import DataLoader from torch import nn, optim from torchvision import transforms, datasets from ae import AE # 引入自编码器 import visdom #可视化 def main(): # #进行下载 # mnist_train = datasets.MNIST('mnist', True, transform=transforms.Compose([ # transforms.ToTensor() # ]), download=True) # # # mnist_test = datasets.MNIST('mnist', False, transform=transforms.Compose([ # transforms.ToTensor() # ]), download=True) # 使用本地的 mnist:processed,raw mnist_train = datasets.MNIST('../../data', train=True, transform=transforms.Compose([ transforms.ToTensor() ]), download=False) mnist_test = datasets.MNIST('../../data', train=False, transform=transforms.Compose([ transforms.ToTensor() ]), download=False) mnist_train = DataLoader(mnist_train, batch_size=32, shuffle=True) mnist_test = DataLoader(mnist_test, batch_size=32, shuffle=True) x, _ = iter(mnist_train).next() print('x:', x.shape) device = torch.device('cuda') model = AE().to(device) # model = VAE().to(device) criteon = nn.MSELoss() # MSE loss 均方差loss optimizer = optim.Adam(model.parameters(), lr=1e-3) # 迭代器 print(model) viz = visdom.Visdom() #可视化 for epoch in range(1000): for batchidx, (x, _) in enumerate(mnist_train): # [b, 1, 28, 28] x = x.to(device) x_hat, kld = model(x) loss = criteon(x_hat, x) # 计算loss # backprop optimizer.zero_grad() # 梯度清零 loss.backward() optimizer.step() # 迭代 # print(epoch, 'loss:', loss.item(), 'kld:', kld.item()) print(epoch, 'loss:', loss.item()) x, _ = iter(mnist_test).next() #test 图片迭代 x = x.to(device) with torch.no_grad(): #不计算 梯度 x_hat, kld = model(x) #模型 计算 viz.images(x, nrow=8, win='x', opts=dict(title='x')) #可视化 x 图 viz.images(x_hat, nrow=8, win='x_hat', opts=dict(title='x_hat')) #可视化 x_hat 图 if __name__ == '__main__': main() ``` VAE实现: ![1629278909087.png](img/1629278909087.png) ## VAE 变分自编码 实现: vae: ```apache #变分 自编码器 import torch from torch import nn class VAE(nn.Module): def __init__(self): super(VAE, self).__init__() # 定义编码器 # [b, 784] => [b, 10] self.encoder = nn.Sequential( nn.Linear(784, 256), nn.ReLU(), nn.Linear(256, 64), nn.ReLU(), nn.Linear(64, 20), nn.ReLU() ) # 定义解码器 # [b, 10] => [b, 784] self.decoder = nn.Sequential( nn.Linear(10, 64), nn.ReLU(), nn.Linear(64, 256), nn.ReLU(), nn.Linear(256, 784), nn.Sigmoid() # 变为0或1的值 ) def forward(self, x): """ :param x: [b, 1, 28, 28] :return: """ batchsz = x.size(0) # 数据输入,指输入第一个 # flatten x = x.view(batchsz, 784) # 打平 # [b, 20], including mean and sigma h_ = self.encoder(x) # 变分 操作 # [b, 20] => [b, 10]均值m/μ 的维度 and [b, 10]方差σ 的维度,h_.chunk做维度拆分 mu, sigma = h_.chunk(2, dim=1) #μ,σ # reparametrize trick重新参数化, epison~N(0, 1),torch.randn_like返回再(0,1)上均匀分布的随机数 h = mu + sigma * torch.randn_like(sigma) #=》[b, 10] # decoder # [b, 10] => [b, 784] x_hat = self.decoder(h) # reshape x_hat = x_hat.view(batchsz, 1, 28, 28) # 恢复数据维度 # Kullback-Leibler散度:让Q(z|X)的分布无限接近于P(X|z)的分布, # 为了确定出这两个分布到底有多接近,我们可以采用两个分布间的Kullback-Leibler散度D来进行度量 # 计算KL损失与MSE损失 # KLD = torch.sum(torch.exp(sigma) - (1 + sigma) + torch.pow(m, 2)) / (input.size(0)*28*28) # KLD = torch.sum(torch.exp(sigma) - (1 + sigma) + torch.pow(m, 2)) # 此公式是直接根据KL Div公式化简,两个分布分别是(0-1)分布与(m,sigma)分布 # 最后根据像素点与样本批次求平均,既realimage.size(0)*28*28 kld = 0.5 * torch.sum( torch.pow(mu, 2) + torch.pow(sigma, 2) - torch.log(1e-8 + torch.pow(sigma, 2)) - 1 #1e-8 避免log无限接近负无穷 ) / (batchsz*28*28) return x_hat, kld ``` main.py: ```apache import torch from torch.utils.data import DataLoader from torch import nn, optim from torchvision import transforms, datasets # from ae import AE # 引入自编码器 from vae import VAE # 引入 变分 自编码器 import visdom # 可视化 def main(): # #进行下载 # mnist_train = datasets.MNIST('mnist', True, transform=transforms.Compose([ # transforms.ToTensor() # ]), download=True) # # # mnist_test = datasets.MNIST('mnist', False, transform=transforms.Compose([ # transforms.ToTensor() # ]), download=True) # 使用本地的 mnist:processed,raw mnist_train = datasets.MNIST('../../data', train=True, transform=transforms.Compose([ transforms.ToTensor() ]), download=False) mnist_test = datasets.MNIST('../../data', train=False, transform=transforms.Compose([ transforms.ToTensor() ]), download=False) mnist_train = DataLoader(mnist_train, batch_size=32, shuffle=True) mnist_test = DataLoader(mnist_test, batch_size=32, shuffle=True) x, _ = iter(mnist_train).next() print('x:', x.shape) device = torch.device('cuda') model = VAE().to(device) # model = VAE().to(device) criteon = nn.MSELoss() # MSE loss 均方差loss optimizer = optim.Adam(model.parameters(), lr=1e-3) # 迭代器 print(model) viz = visdom.Visdom() # 可视化 for epoch in range(1000): for batchidx, (x, _) in enumerate(mnist_train): # [b, 1, 28, 28] x = x.to(device) x_hat, kld = model(x) loss = criteon(x_hat, x) # 计算均方差损失 if kld is not None: # vae计算 kld散度 elbo = - loss - 1.0 * kld # 总的损失函数 loss = - elbo # 总的损失函数 变为正的 # backprop optimizer.zero_grad() # 梯度清零 loss.backward() optimizer.step() # 迭代 print(epoch, 'loss:', loss.item(), 'kld散度:', kld.item()) x, _ = iter(mnist_test).next() # test 图片迭代 x = x.to(device) with torch.no_grad(): # 不计算 梯度 x_hat, kld = model(x) # 模型 计算 viz.images(x, nrow=8, win='x', opts=dict(title='x')) # 可视化 x 图 viz.images(x_hat, nrow=8, win='x_hat', opts=dict(title='x_hat')) # 可视化 x_hat 图 if __name__ == '__main__': main() ``` # GAN-生成对抗网络 ## 基本介绍: 适用于图像的卷积神经网络,适用于序列的循环神经网络。但是要知道Lecun提出第一代卷积网络Lenet的时间是1998年,而循环神经网络提出的时间更早,是在1986年。这些网络在当时并没有火起来,如今随着计算能力的加强,数据集的增多,深度学习逐渐火了起来,随着越来越多的人的研究,各种各样的神经网络都在不断进步,CNN里面出现了inception net,resnet等等,RNN演变了LSTM和GRU,虽然神经网络不断在发展,但是本质上仍然是在CNN和RNN的基础上。 直到2014年,深度学习三巨头之一 Ian Goodfellow 提出了**生成对抗网络(Generative Adversarial Networks, GANs)** GANs(生成对抗网络),顾名思义,这个网络第一部分是生成网络,第二部分对抗模型严格来讲是一个判别器;简单来说,就是让两个网络相互竞争,生成网络来生成假的数据,对抗网络通过判别器去判别真伪,最后希望生成器生成的数据能够以假乱真。 **Discriminator Network** 首先我们来讲一下对抗过程,因为这个过程更加简单。 对抗过程简单来说就是一个判断真假的判别器,相当于一个二分类问题,我们输入一张真的图片希望判别器输出的结果是1,输入一张假的图片希望判别器输出的结果是0。这其实已经和原图片的label没有关系了,不管原图片到底是一个多少类别的图片,他们都统一称为真的图片,label是1表示真实的;而生成的假的图片的label是0表示假的。 我们训练的过程就是希望这个判别器能够正确的判出真的图片和假的图片,这其实就是一个简单的二分类问题,对于这个问题可以用我们前面讲过的很多方法去处理,比如logistic回归,深层网络,卷积神经网络,循环神经网络都可以。 **Generative Network** 接着我们要看看如何生成一张假的图片。首先给出一个简单的高维的正态分布的噪声向量,如上图所示的D-dimensional noise vector,这个时候我们可以通过仿射变换,也就是xw+b将其映射到一个更高的维度,然后将他重新排列成一个矩形,这样看着更像一张图片,接着进行一些卷积、池化、激活函数处理,最后得到了一个与我们输入图片大小一模一样的噪音矩阵,这就是我们所说的假的图片,这个时候我们如何去训练这个生成器呢?就是通过判别器来得到结果,然后希望增大判别器判别这个结果为真的概率,在这一步我们不会更新判别器的参数,只会更新生成器的参数。 **为何Gans能够成为最近20年来机器学习以及深度学习界革命性的发现。** 这是因为不管是深度学习还是机器学习仍然很大一部分是监督学习, 但是创建这么多有label的数据集所需要的人力物力是极大的, 同时遇到的新的任务时我们很容易得到原始的没有label的数据集,这是我们需要花大量的时间去给其标定label,所以很多人都认为无监督学习才是机器学习的未来, 这个时候**Gans的出现为无监督学习提供了有力的支持**,这当然引起了学界的大量关注,同时基于Gans的应用也越来越多,业界对其也非常狂热。 最后引用Yan Lecun的话:”它(Gans)为创建无监督学习模型提供了强有力的算法框架,有望帮助我们为 AI 加入常识(common sense)。我们认为,沿着这条路走下去,有不小的成功机会能开发出更智慧的 AI 。 ## GAN: ![1629337716610.png](img/1629337716610.png) ![1629337965596.png](img/1629337965596.png) ![1629338409686.png](img/1629338409686.png) 通过不断学习,到达了纳什均衡点: ![1629338216078.png](img/1629338216078.png) ![1629338780834.png](img/1629338780834.png) ![1629338887701.png](img/1629338887701.png) big gan: https://drive.google.com/drive/folders/1lWC6XEPD0LT5KUnPXeve_kWeY-FxH002 ![1629338965441.png](img/1629338965441.png) Having Fun: ▪ https://reiinakano.github.io/gan playground/ ▪ https://affinelayer.com/pixsrv/ ▪ https://www.youtube.com/watch?v=9reHvktowLY&feature=youtu.be ▪ https://github.com/ajbrock/Neural Photo Editor ▪ https://github.com/nashory/gans awesome applications 在线训练 示例: ![1629339243268.png](img/1629339243268.png) ![1629339364311.png](img/1629339364311.png) ![1629339587721.png](img/1629339587721.png) -D: ![1629340686783.png](img/1629340686783.png) ![1629340752860.png](img/1629340752860.png) -G: 取得最小值:Pg=Pr的时候,D*也等于1/2了--》到达了纳什均衡点 ![1629341239817.png](img/1629341239817.png) ![1629341482933.png](img/1629341482933.png) G: 取得最小值:Pg=Pr的时候,D*也等于1/2了--》到达了纳什均衡点 ![1629341619315.png](img/1629341619315.png) GAN的历史论文分布: https://github.com/hindupuravinash/the-gan-zoo/blob/master/cumulative_gans.jpg 影响比较大的变种: DCGAN: https://blog.openai.com/generative-models/ ![1629341929504.png](img/1629341929504.png) ![1629342051713.png](img/1629342051713.png) 稳定性差: ![1629342284791.png](img/1629342284791.png) ![1629342555358.png](img/1629342555358.png) ![1629342697587.png](img/1629342697587.png) ![1629342748052.png](img/1629342748052.png) ![1629342821187.png](img/1629342821187.png) ![1629342897497.png](img/1629342897497.png) EM距离: ![1629343143330.png](img/1629343143330.png) 换了评价函数: ![1629343362727.png](img/1629343362727.png) ## WGAN-GP: ![1629343945727.png](img/1629343945727.png) 效果,稳定了不少: ![1629343972094.png](img/1629343972094.png) ## GAN实现: ```apache #**首先设置seed 避免随机性 结果差别太大** torch.manual_seed(23) np.random.seed(23) ``` gan的实现: ```apache import torch from torch import nn, optim, autograd # autograd 自动求导 import numpy as np import visdom from torch.nn import functional as F from matplotlib import pyplot as plt import random # np 中的 random h_dim = 400 # h层的维度 batchsz = 512 # 批次数 viz = visdom.Visdom() # 生成网络 class Generator(nn.Module): def __init__(self): super(Generator, self).__init__() # z:[b,2]==>[b,2] self.net = nn.Sequential( nn.Linear(2, h_dim), nn.ReLU(True), # inplace为True,将会改变输入的数据 ,否,不会改变原输入,只会产生新的输出 nn.Linear(h_dim, h_dim), nn.ReLU(True), nn.Linear(h_dim, h_dim), nn.ReLU(True), nn.Linear(h_dim, 2), ) def forward(self, z): output = self.net(z) return output # 评估网络 class Discriminator(nn.Module): def __init__(self): super(Discriminator, self).__init__() self.net = nn.Sequential( nn.Linear(2, h_dim), nn.ReLU(True), nn.Linear(h_dim, h_dim), nn.ReLU(True), nn.Linear(h_dim, h_dim), nn.ReLU(True), nn.Linear(h_dim, 1), nn.Sigmoid() # 输出为0或者1 ) def forward(self, x): output = self.net(x) return output.view(-1) # 自定义数据集:8-gaussian mixture models def data_generator(): scale = 2. centers = [ (1, 0), (-1, 0), (0, 1), (0, -1), (1. / np.sqrt(2), 1. / np.sqrt(2)), (1. / np.sqrt(2), -1. / np.sqrt(2)), (-1. / np.sqrt(2), 1. / np.sqrt(2)), (-1. / np.sqrt(2), -1. / np.sqrt(2)) ] centers = [(scale * x, scale * y) for x, y in centers] while True: dataset = [] # 数据集 for i in range(batchsz): point = np.random.randn(2) * .02 center = random.choice(centers) # 随机 选择 中心点数据 point[0] += center[0] point[1] += center[1] dataset.append(point) dataset = np.array(dataset, dtype='float32') dataset /= 1.414 # stdev yield dataset # yield会从上一个循环继续开始循环 # Generates and saves a plot of the true distribution, the generator, and the critic. def generate_image(D, G, xr, epoch): """ Generates and saves a plot of the true distribution, the generator, and the critic. """ N_POINTS = 128 RANGE = 3 plt.clf() points = np.zeros((N_POINTS, N_POINTS, 2), dtype='float32') points[:, :, 0] = np.linspace(-RANGE, RANGE, N_POINTS)[:, None] points[:, :, 1] = np.linspace(-RANGE, RANGE, N_POINTS)[None, :] points = points.reshape((-1, 2)) # (16384, 2) # print('p:', points.shape) # draw contour with torch.no_grad(): points = torch.Tensor(points).cuda() # [16384, 2] disc_map = D(points).cpu().numpy() # [16384] x = y = np.linspace(-RANGE, RANGE, N_POINTS) cs = plt.contour(x, y, disc_map.reshape((len(x), len(y))).transpose()) plt.clabel(cs, inline=1, fontsize=10) # plt.colorbar() # draw samples with torch.no_grad(): z = torch.randn(batchsz, 2).cuda() # [b, 2] samples = G(z).cpu().numpy() # [b, 2] plt.scatter(xr[:, 0], xr[:, 1], c='orange', marker='.') plt.scatter(samples[:, 0], samples[:, 1], c='green', marker='+') viz.matplot(plt, win='contour', opts=dict(title='p(x):%d' % epoch)) def main(): # **首先设置seed 避免随机性 结果差别太大** torch.manual_seed(23) np.random.seed(23) data_iter = data_generator() # 生成数据 # x = next(data_iter) # #(b, 2) # print(x.shape) #(512, 2) G = Generator().cuda() # .cuda() 使用cuda D = Discriminator().cuda() # print(G) #看出model结构 # print(D) # 优化器的设置 optim_G = optim.Adam(G.parameters(), lr=1e-3, betas=(0.5, 0.9)) optim_D = optim.Adam(D.parameters(), lr=1e-3, betas=(0.5, 0.9)) viz.line([[0, 0]], [0], win='loss', opts=dict(title='loss', legend=['D', 'G'])) for epoch in range(50000): # 1. train discriminator for k steps for _ in range(5): # 1.1 train on real data x = next(data_iter) # 使用数据集 xr = torch.from_numpy(x).cuda() # 把numpy数据转换为torch 用的tensor,并给GPU predr = (D(xr)) # 评估网络 计算pre值 # [b,2]=>[b,1] lossr = - (predr.mean()) # max log(lossr) ,loss计算,因为是求Max值,所以把mean变为-mean # 1.2 train on fake data 把生成的数据 给评估网络 看loss z = torch.randn(batchsz, 2).cuda() # [b, 2] xf = G(z).detach() # # [b, 2] ,.detach():stop gradient on G 类似tf.stop_gradient() predf = (D(xf)) # [b] lossf = (predf.mean()) # min predf # aggregate all 综合loss loss_D = lossr + lossf optim_D.zero_grad() loss_D.backward() optim_D.step() # 优化器迭代 # 2. train Generator z = torch.randn(batchsz, 2).cuda() # z xf = G(z) # 生成网络 的数据 predf = (D(xf)) # 评估网络 计算pre值 loss_G = - (predf.mean()) # 找min值,所以对D的mean负数 optim_G.zero_grad() loss_G.backward() optim_G.step() if epoch % 100 == 0: viz.line([[loss_D.item(), loss_G.item()]], [epoch], win='loss', update='append') generate_image(D, G, xr.cpu().numpy(), epoch) print(loss_D.item(), loss_G.item()) if __name__ == '__main__': main() ``` ![1629356728843.png](img/1629356728843.png) ![1629356738871.png](img/1629356738871.png) ## WGAN-GP 实现: 实现: ```apache import torch from torch import nn, optim, autograd # autograd 自动求导 import numpy as np import visdom from torch.nn import functional as F from matplotlib import pyplot as plt import random # np 中的 random h_dim = 400 # h层的维度 batchsz = 512 # 批次数 viz = visdom.Visdom() # 生成网络 class Generator(nn.Module): def __init__(self): super(Generator, self).__init__() # z:[b,2]==>[b,2] self.net = nn.Sequential( nn.Linear(2, h_dim), nn.ReLU(True), # inplace为True,将会改变输入的数据 ,否,不会改变原输入,只会产生新的输出 nn.Linear(h_dim, h_dim), nn.ReLU(True), nn.Linear(h_dim, h_dim), nn.ReLU(True), nn.Linear(h_dim, 2), ) def forward(self, z): output = self.net(z) return output # 评估网络 class Discriminator(nn.Module): def __init__(self): super(Discriminator, self).__init__() self.net = nn.Sequential( nn.Linear(2, h_dim), nn.ReLU(True), nn.Linear(h_dim, h_dim), nn.ReLU(True), nn.Linear(h_dim, h_dim), nn.ReLU(True), nn.Linear(h_dim, 1), nn.Sigmoid() # 输出为0或者1 ) def forward(self, x): output = self.net(x) return output.view(-1) # 自定义数据集:8-gaussian mixture models def data_generator(): scale = 2. centers = [ (1, 0), (-1, 0), (0, 1), (0, -1), (1. / np.sqrt(2), 1. / np.sqrt(2)), (1. / np.sqrt(2), -1. / np.sqrt(2)), (-1. / np.sqrt(2), 1. / np.sqrt(2)), (-1. / np.sqrt(2), -1. / np.sqrt(2)) ] centers = [(scale * x, scale * y) for x, y in centers] while True: dataset = [] # 数据集 for i in range(batchsz): point = np.random.randn(2) * .02 center = random.choice(centers) # 随机 选择 中心点数据 point[0] += center[0] point[1] += center[1] dataset.append(point) dataset = np.array(dataset, dtype='float32') dataset /= 1.414 # stdev yield dataset # yield会从上一个循环继续开始循环 # Generates and saves a plot of the true distribution, the generator, and the critic. def generate_image(D, G, xr, epoch): """ Generates and saves a plot of the true distribution, the generator, and the critic. """ N_POINTS = 128 RANGE = 3 plt.clf() points = np.zeros((N_POINTS, N_POINTS, 2), dtype='float32') points[:, :, 0] = np.linspace(-RANGE, RANGE, N_POINTS)[:, None] points[:, :, 1] = np.linspace(-RANGE, RANGE, N_POINTS)[None, :] points = points.reshape((-1, 2)) # (16384, 2) # print('p:', points.shape) # draw contour with torch.no_grad(): points = torch.Tensor(points).cuda() # [16384, 2] disc_map = D(points).cpu().numpy() # [16384] x = y = np.linspace(-RANGE, RANGE, N_POINTS) cs = plt.contour(x, y, disc_map.reshape((len(x), len(y))).transpose()) plt.clabel(cs, inline=1, fontsize=10) # plt.colorbar() # draw samples with torch.no_grad(): z = torch.randn(batchsz, 2).cuda() # [b, 2] samples = G(z).cpu().numpy() # [b, 2] plt.scatter(xr[:, 0], xr[:, 1], c='orange', marker='.') plt.scatter(samples[:, 0], samples[:, 1], c='green', marker='+') viz.matplot(plt, win='contour', opts=dict(title='p(x):%d' % epoch)) def gradient_penalty(D, xr, xf): # 梯度惩罚:xr真实数据 与 xf假数据直接插值 """ :param D: :param xr:[b,2] :param xf:[b,2] :return: """ # [b,1] t = torch.rand(batchsz, 1).cuda() # [b,1]=>[b,2] t = t.expand_as(xr) # 扩展为 与xr相同的维度 mid = t * xr + (1 - t) * xf # interpolation 做插值 mid.requires_grad_() # 需要导数信息 pred = D(mid) # 插值后的 评估值 # 求导 grads = autograd.grad(outputs=pred, inputs=mid, grad_outputs=torch.ones_like(pred), create_graph=True, retain_graph=True, only_inputs=True)[0] gp = torch.pow(grads.norm(2, dim=1) - 1, 2).mean() # 求导后的2范数 减1,平方,的均值 return gp def main(): # **首先设置seed 避免随机性 结果差别太大** torch.manual_seed(23) np.random.seed(23) data_iter = data_generator() # 生成数据 # x = next(data_iter) # #(b, 2) # print(x.shape) #(512, 2) G = Generator().cuda() # .cuda() 使用cuda D = Discriminator().cuda() # print(G) #看出model结构 # print(D) # 优化器的设置 optim_G = optim.Adam(G.parameters(), lr=1e-3, betas=(0.5, 0.9)) optim_D = optim.Adam(D.parameters(), lr=1e-3, betas=(0.5, 0.9)) viz.line([[0, 0]], [0], win='loss', opts=dict(title='loss', legend=['D', 'G'])) for epoch in range(50000): # 1. train discriminator for k steps for _ in range(5): # 1.1 train on real data x = next(data_iter) # 使用数据集 xr = torch.from_numpy(x).cuda() # 把numpy数据转换为torch 用的tensor,并给GPU predr = (D(xr)) # 评估网络 计算pre值 # [b,2]=>[b,1] lossr = - (predr.mean()) # max log(lossr) ,loss计算,因为是求Max值,所以把mean变为-mean # 1.2 train on fake data 把生成的数据 给评估网络 看loss z = torch.randn(batchsz, 2).cuda() # [b, 2] xf = G(z).detach() # # [b, 2] ,.detach():stop gradient on G 类似tf.stop_gradient() predf = (D(xf)) # [b] lossf = (predf.mean()) # min predf # 1.3 gradient_penalty gp = gradient_penalty(D, xr, xf.detach()) # 梯度惩罚:xr真实数据 与 xf假数据 直接插值 评估网络计算后的梯度 xf.detach():不把梯度信息代入 # aggregate all 综合loss loss_D = lossr + lossf + 0.2 * gp # 0.2 为λ参数 optim_D.zero_grad() loss_D.backward() optim_D.step() # 优化器迭代 # 2. train Generator z = torch.randn(batchsz, 2).cuda() # z xf = G(z) # 生成网络 的数据 predf = (D(xf)) # 评估网络 计算pre值 loss_G = - (predf.mean()) # 找min值,所以对D的mean负数 optim_G.zero_grad() loss_G.backward() optim_G.step() if epoch % 100 == 0: viz.line([[loss_D.item(), loss_G.item()]], [epoch], win='loss', update='append') generate_image(D, G, xr.cpu().numpy(), epoch) print(loss_D.item(), loss_G.item()) if __name__ == '__main__': main() ``` ![1629360773641.png](img/1629360773641.png) ![1629360787241.png](img/1629360787241.png) ====进行过3周,终于学习完了,还是要找代码实践~ # 使用google CoLab:12h的免费GPU ![1629180738876.png](img/1629180738876.png) # opencv4nodejs: ## 安装,--真是老费劲的 1.首先需要全局的上网,不要绕行ip,不然下载文件什么的,都是问题:这些文件不好下载下来, 剩下的就是安装 npm install --global --production windows-build-tools npm config set msvs_version 2017 npm install opencv4nodejs 其中:直接执行,会自动安装opencv3.4.6版本的 2.可以指定opencv版本,将自动安装的opencv build文件夹复制出来,并在环境变量里面指定 set OPENCV4NODEJS_DISABLE_AUTOBUILD=1 //不自动安装,自己下载opencv,opencv_contrib并build文件,在设置环境变量。。 在进行npm install opencv4nodejs 安装 3.examples运行, https://github.com/justadudewhohacks/opencv4nodejs 上面examples例子问题, 他好多是路径设置错误,需要修改: 1.模块写错了: if (!cv.xmodules.face) { 改成 if (!cv.modules.face) { 去掉x就对了。。nnd弄了这个长时间 2.文件路径问题: const basePath = '../data/face-recognition'; 要改为const basePath = './data/face-recognition'; 去掉其中的点. opencv4nodejs安装,运行例子 git clone https://github.com/justadudewhohacks/opencv4nodejs.git git clone https://github.com.cnpmjs.org/justadudewhohacks/opencv4nodejs.git cd opencv4nodejs npm i 成功后 可以运行examples上面的例子 https://github.com/Robintjhb/opencv4nodejs_test ## 安装问题: process.dlopen: internal/modules/cjs/loader.js:1025 return process.dlopen(module, path.toNamespacedPath(filename)); 通过这个解决: https://github.com/justadudewhohacks/opencv4nodejs/issues/125 js文件运行:console.log(process.env.path); cmd下:echo %path% 上面两个opencv bin 位置结果这两个要相同: 应用案例: