首页
关于
Search
1
图神经网络
68 阅读
2
java期末速成
30 阅读
3
CLIP
29 阅读
4
Attention2Transformer
26 阅读
5
MySQL
23 阅读
默认分类
AI
课内
技能
Search
标签搜索
AI
CS
Tools
paper
DeepLearning
python
DATA
GNN
Transformer
hadoop
晨旭不想写程序
累计撰写
22
篇文章
累计收到
13
条评论
首页
栏目
默认分类
AI
课内
技能
页面
关于
搜索到
3
篇与
的结果
2024-06-02
CLIP
CLIP论文精读CLIP是什么一个强大的无监督训练模型通过NLP来的监督信号得到迁移学习进行图片与文字的配对实现监督的信号,解决了需要打标签进行训练的限制,增强了模型的泛化能力CLIP结构CLIP的结构包含两个模型Text Encoder和Image Encoder,Text Encoder用于提取文本特征,Image Encoder用来提取图像特征CLIP训练CLIP的训练数据是图像-文本对,如图上方是对小狗的描述,而下方是这张图片,通过对文本的特征提取与对图像的特征提取进行对比学习,对于N个图像文字对,预测出$N^2$个相似度,这里的相似度直接结算文本特征和图像特征的余弦相似性,实际上真实对应的相似对是位于对角线上的元素,我们的目的就是最大化对角线上的元素而减小非对角线上的元素实现zero-shot分类首先先将分类标签扩充成句子后输入到 TextEncoder中,而进行分类时的标签并不需要是训时存在的标签 ,你完全可以新加一个背带裤的标签进行分类,训练与推理时都没有标签的限制,属实是将视觉与文字的语义相关性真正学习到了。使用clip可以辅助实现风格迁移,AI换脸换衣,图像检测 分割,视频检索论文部分采用有限制性的监督信号会限制模型的泛化性这一点毋庸置疑 ,要识别新的物体类别时候就有了困难所以CLIP的想法就是由语言生成监督信号 经过测试,CLIP在ImageNet上可以跟专门为了ImageNet训练出来的resnet50打成平手 达到了非常好的效果并且可以随着两个模型性能继续增长后可以达到不断的进步从文本出来的弱监督信号不实用,是因为数据量不足够与算力消耗大方法上实际上都差不多,但是数据量规模是其想成长的必要因素像VirTex,ICMLM,ConVIRT这些工作都进行过类似的尝试,但是都是只训练了几天的规模,并不足以达到很好的效果于是openAI团队为了证明这一点 收集了超级大规模的数据想要达到比较好的效果再加上大模型的加持,可以达到非常不错的效果,这就是CLIP(Contrastive Language-Image Pre-training)对于模型选择,作者团队也尝试了多种的尝试,发现CLIP的效果跟模型规模是有正相关的最终得到的效果是,CLIP在30多个数据集上基本都能与精心设计的模型打成平手甚至胜利,并且会有更好的泛化性使用CLIP的好处有很多,其中之一就是CLIP不需要再对数据进行标注,只需要获得文本—图像对就可以,像在社交平台上获得的图片跟他发布时的TAG就是一个很好的途径,这种数据往往比标注的数据更容易获取,另外,通过文本—图像对的这种数据集训练,使得其拥有了多模态的效果 在预训练过程中,作者团队采用了对比学习的方法,之所以使用这样的方法而不是用GPT就是因为语言的多样性导致对应关系有很多(例如一张图片可以从多个角度描述),所以我们只需要让图片与文本配对即可,通过这样就能达到很高的效率代码的实现在实现方面,通过论文所给伪代码# image_encoder - ResNet or Vision Transformer # text_encoder - CBOW or Text Transformer # I[n, h, w, c] - minibatch of aligned images # T[n, l] - minibatch of aligned texts # W_i[d_i, d_e] - learned proj of image to embed # W_t[d_t, d_e] - learned proj of text to embed # t - learned temperature parameter # 分别提取图像特征和文本特征 I_f = image_encoder(I) #[n, d_i] T_f = text_encoder(T) #[n, d_t] # 在得到特征时一般会尝试归一化 在归一化前,还涉及到了投射层,即np.dot(I_f, W_i),主要用来学习如何从单模态投射到多模态 # 对两个特征进行线性投射,得到相同维度的特征,并进行l2归一化 I_e = l2_normalize(np.dot(I_f, W_i), axis=1) T_e = l2_normalize(np.dot(T_f, W_t), axis=1) # 计算缩放的余弦相似度:[n, n] logits = np.dot(I_e, T_e.T) * np.exp(t) # 对称的对比学习损失:等价于N个类别的cross_entropy_loss labels = np.arange(n) # 对角线元素的labels loss_i = cross_entropy_loss(logits, labels, axis=0) loss_t = cross_entropy_loss(logits, labels, axis=1) loss = (loss_i + loss_t)/2步骤解释提取图像特征和文本特征:使用预训练的 image_encoder 和 text_encoder 分别提取图像 I 和文本 T 的特征,得到形状为 [n, d_i] 和 [n, d_t] 的特征向量。线性投射和归一化:对两个特征进行线性投射,分别用矩阵 W_i 和 W_t,得到相同维度的特征向量 I_e 和 T_e。对投射后的特征进行 l2 归一化,保证它们具有单位长度。计算缩放的余弦相似度:通过计算余弦相似度矩阵,得到形状为 [n, n] 的 logits 矩阵。这一步涉及将图像特征和文本特征进行相似度计算,并使用温度参数 t 进行缩放。对称的对比学习损失:创建标签 labels,其中包含了元素从 0 到 n-1。计算两个方向上的交叉熵损失:loss_i 是以图像为查询,文本为正样本的损失;loss_t 是以文本为查询,图像为正样本的损失。最终的损失是两个方向上损失的平均值,即 (loss_i + loss_t) / 2。这个损失函数的设计旨在通过最大化正样本之间的相似度、最小化负样本之间的相似度,来学习图像和文本之间的语义对应关系。这种对称性的设计可以帮助提升模型的泛化能力,使得图像和文本之间的表示更加一致和可靠。
2024年06月02日
29 阅读
0 评论
1 点赞
2024-06-02
动手学深度学习
深度学习环境安装在之前做数据分析就已经安装过anaconda,所以不安装miniconda安装完成之后,由于国内的pip太慢,所以要更换镜像源上海交通大学 Linux 用户组 软件源镜像服务 (sjtu.edu.cn)然后经过一些配置之后安装git也弄过了国内git速度巨慢无比,加速在此GitHub Proxy 代理加速 (ghproxy.com)数学基础学习了高等数学中的微积分模块与对于梯度的学习,链式法则,补习了线代范式内容,后续会更新数学笔记课程环境搭建已搭建成功准备工作数据操作张量张量表示一个由数值组成的数组,这个数组可能有多个维度。 具有一个轴的张量对应数学上的向量(vector); 具有两个轴的张量对应数学上的矩阵(matrix); 具有两个轴以上的张量没有特殊的数学名称。一维张量 对应向量二维张量 对应矩阵三维以及以上不命名重要函数reshape要想改变一个张量的形状而不改变元素数量和元素值,可以调用reshape函数。 例如,可以把张量x从形状为(12,)的行向量转换为形状为(3,4)的矩阵。 这个新的张量包含与转换前相同的值,但是它被看成一个3行4列的矩阵。 要重点说明一下,虽然张量的形状发生了改变,但其元素值并没有变。 注意,通过改变张量的形状,张量的大小不会改变。x = torch.arange(12) x X = x.reshape(3, 4) Xtensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) tensor([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]])我们不需要通过手动指定每个维度来改变形状。 也就是说,如果我们的目标形状是(高度,宽度), 那么在知道宽度后,高度会被自动计算得出,不必我们自己做除法。 在上面的例子中,为了获得一个3行的矩阵,我们手动指定了它有3行和4列。 幸运的是,我们可以通过-1来调用此自动计算出维度的功能。 即我们可以用x.reshape(-1,4)或x.reshape(3,-1)来取代x.reshape(3,4)。zeros,ones,randn有时,我们希望使用全0、全1、其他常量,或者从特定分布中随机采样的数字来初始化矩阵。 我们可以创建一个形状为(2,3,4)的张量,其中所有元素都设置为0。代码如下:torch.zeros((2, 3, 4)) torch.ones((2, 3, 4))tensor([[[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]], [[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]]]) tensor([[[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]], [[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]]])有时我们想通过从某个特定的概率分布中随机采样来得到张量中每个元素的值。 例如,当我们构造数组来作为神经网络中的参数时,我们通常会随机初始化参数的值。 以下代码创建一个形状为(3,4)的张量。 其中的每个元素都从均值为0、标准差为1的标准高斯分布(正态分布)中随机采样。torch.randn(3, 4)tensor([[ 0.7277, -1.3848, -0.2607, 0.9701], [-2.3290, -0.3754, 0.2457, 0.0760], [-1.2832, -0.3600, -0.3321, 0.8184]])运算张量可以直接通过运算符直接按元素计算x = torch.tensor([1.0, 2, 4, 8]) y = torch.tensor([2, 2, 2, 2]) x + y, x - y, x * y, x / y, x ** y (tensor([ 3., 4., 6., 10.]), tensor([-1., 0., 2., 6.]), tensor([ 2., 4., 8., 16.]), tensor([0.5000, 1.0000, 2.0000, 4.0000]), tensor([ 1., 4., 16., 64.]))还可以通过函数连结在一起,连结时选择参数,按照轴0还是轴1,按照轴0就是按行结合,1是按照列结合X = torch.arange(12, dtype=torch.float32).reshape((3,4)) Y = torch.tensor([[2.0, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]]) torch.cat((X, Y), dim=0), torch.cat((X, Y), dim=1)(tensor([[ 0., 1., 2., 3.], [ 4., 5., 6., 7.], [ 8., 9., 10., 11.], [ 2., 1., 4., 3.], [ 1., 2., 3., 4.], [ 4., 3., 2., 1.]]), tensor([[ 0., 1., 2., 3., 2., 1., 4., 3.], [ 4., 5., 6., 7., 1., 2., 3., 4.], [ 8., 9., 10., 11., 4., 3., 2., 1.]]))还可以通过逻辑运算符来获得特殊张量X == Ytensor([[False, True, False, True], [False, False, False, False], [False, False, False, False]])在某些情况下,即使形状不同,我们仍然可以通过调用 广播机制(broadcasting mechanism)来执行按元素操作。 这种机制的工作方式如下(就是逐行去逐列进行计算):1.通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状;2.对生成的数组执行按元素操作。a = torch.arange(3).reshape((3, 1)) b = torch.arange(2).reshape((1, 2)) a, b a + b(tensor([[0], [1], [2]]), tensor([[0, 1]])) tensor([[0, 1], [1, 2], [2, 3]])索引跟切片与普通列表大差不差,所以不对其再进行论述节省内存一些操作可能会导致新结果分配新的内存,在对大量数据进行操作时,会占用很大的存储空间,故采用方法进行规避before = id(Y) Y = Y + X id(Y) == beforeFalse综上,这样加合的习惯会带来不小的麻烦,我们采取原地操作执行原地操作非常简单。 我们可以使用切片表示法将操作的结果分配给先前分配的数组如果在后续计算中没有重复使用X, 我们也可以使用X[:] = X + Y或X += Y来减少操作的内存开销。数据预处理在学习中,我们要使用pandas预处理原始数据,并将原始数据转换为张量格式首先我们无论自己手动编写,还是外界下载,要得到一个CSV格式的文件通过pandas的read_csv函数对数据进行格式转化pandas.read_csv(文件)处理缺失值在数据处理中,会有一些值由于某种原因缺失,这种情况下我们要对其进行一定的处理。通常用的有两种方法,插值法和删除法, 其中插值法用一个替代值弥补缺失值,而删除法则直接忽略缺失值。 在这里,我们将考虑插值法。例如这样的一个表 NumRooms Alley Price 0 NaN Pave 127500 1 2.0 NaN 106000 2 4.0 NaN 178100 3 NaN NaN 140000通过位置索引iloc,我们将data分成inputs和outputs, 其中前者为data的前两列,而后者为data的最后一列。 对于inputs中缺少的数值,我们用同一列的均值替换“NaN”项。inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2] inputs = inputs.fillna(inputs.mean()) print(inputs) NumRooms Alley 0 3.0 Pave 1 2.0 NaN 2 4.0 NaN 3 3.0 NaN对于inputs中的类别值或离散值,我们将“NaN”视为一个类别。 由于“巷子类型”(“Alley”)列只接受两种类型的类别值“Pave”和“NaN”, pandas可以自动将此列转换为两列“Alley_Pave”和“Alley_nan”。 巷子类型为“Pave”的行会将“Alley_Pave”的值设置为1,“Alley_nan”的值设置为0。 缺少巷子类型的行会将“Alley_Pave”和“Alley_nan”分别设置为0和1。inputs = pd.get_dummies(inputs, dummy_na=True) print(inputs) NumRooms Alley_Pave Alley_nan 0 3.0 1 0 1 2.0 0 1 2 4.0 0 1 3 3.0 0 1现在inputs和outputs中的所有条目都是数值类型,它们可以转换为张量格式。import torch X, y = torch.tensor(inputs.values), torch.tensor(outputs.values) X, y(tensor([[3., 1., 0.], [2., 0., 1.], [4., 0., 1.], [3., 0., 1.]], dtype=torch.float64), tensor([127500, 106000, 178100, 140000]))线性代数数学知识之前已经了解了表示标量向量矩阵的方法,接下来不算总结,仅为摘要书上内容线性代数计算的实现1.矩阵转置.t例子:A = torch.arange(20).reshape(5, 4) Atensor([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11], [12, 13, 14, 15], [16, 17, 18, 19]])A.Ttensor([[ 0, 4, 8, 12, 16], [ 1, 5, 9, 13, 17], [ 2, 6, 10, 14, 18], [ 3, 7, 11, 15, 19]])2.降维.sum对于向量,降维就是访问sumx = torch.arange(4, dtype=torch.float32) x, x.sum()(tensor([0., 1., 2., 3.]), tensor(6.))默认情况下,调用求和函数会沿所有的轴降低张量的维度,使它变为一个标量。 我们还可以指定张量沿哪一个轴来通过求和降低维度。 以矩阵为例,为了通过求和所有行的元素来降维(轴0),可以在调用函数时指定axis=0。 由于输入矩阵沿0轴降维以生成输出向量,因此输入轴0的维数在输出形状中消失。A_sum_axis0 = A.sum(axis=0) A_sum_axis0, A_sum_axis0.shape(tensor([40., 45., 50., 55.]), torch.Size([4]))指定axis=1将通过汇总所有列的元素降维(轴1)。因此,输入轴1的维数在输出形状中消失。A_sum_axis1 = A.sum(axis=1) A_sum_axis1, A_sum_axis1.shape(tensor([ 6., 22., 38., 54., 70.]), torch.Size([5]))3.平均值.meanA.mean()(tensor(9.5000), tensor(9.5000))4.非降维求和sum_A = A.sum(axis=1, keepdims=True) sum_Atensor([[ 6.], [22.], [38.], [54.], [70.]]).cumsumA.cumsum(axis=0)tensor([[ 0., 1., 2., 3.], [ 4., 6., 8., 10.], [12., 15., 18., 21.], [24., 28., 32., 36.], [40., 45., 50., 55.]])5.点积采用按元素乘的和即可y = torch.ones(4, dtype = torch.float32) x, y, torch.dot(x, y)(tensor([0., 1., 2., 3.]), tensor([1., 1., 1., 1.]), tensor(6.))torch.sum(x * y)tensor(6.)6.矩阵-向量积.mv在代码中使用张量表示矩阵-向量积,我们使用mv函数。 当我们为矩阵A和向量x调用torch.mv(A, x)时,会执行矩阵-向量积。 注意,A的列维数(沿轴1的长度)必须与x的维数(其长度)相同。A.shape, x.shape, torch.mv(A, x)(torch.Size([5, 4]), torch.Size([4]), tensor([ 14., 38., 62., 86., 110.]))7.矩阵-矩阵乘法.mmB = torch.ones(4, 3) torch.mm(A, B)tensor([[ 6., 6., 6.], [22., 22., 22.], [38., 38., 38.], [54., 54., 54.], [70., 70., 70.]])矩阵-矩阵乘法可以简单地称为矩阵乘法,不应与”Hadamard积(就是同位置简单相乘)”混淆。8.范式线性代数中最有用的一些运算符是范数(norm)。 非正式地说,向量的范数是表示一个向量有多大。 这里考虑的大小(size)概念不涉及维度,而是分量的大小。.abs().sum()L1范式的使用torch.abs(u).sum()tensor(7.).normL2范式的使用u = torch.tensor([3.0, -4.0]) torch.norm(u)tensor(5.)线性回归首先是书上基础的概念它在回归的各种标准工具中最简单而且最流行。 线性回归基于几个简单的假设: 首先,假设自变量x和因变量y之间的关系是线性的, 即y可以表示为x中元素的加权和,这里通常允许包含观测值的一些噪声; 其次,我们假设任何噪声都比较正常,如噪声遵循正态分布。线性模型线性模型很简单,就是加权和,是各个参数经过加权后再加上偏置得到的值,主要是对于参数与权重的确定,而偏置是对其准确性的调整损失函数损失函数(loss function)能够量化目标的实际值与预测值之间的差距。 通常我们会选择非负数作为损失,且数值越小表示损失越小,完美预测时的损失为0。 回归问题中最常用的损失函数是平方误差函数。解析解线性回归刚好是一个很简单的优化问题。 与我们将在本书中所讲到的其他大部分模型不同,线性回归的解可以用一个公式简单地表达出来, 这类解叫作解析解(analytical solution)。像线性回归这样的简单问题存在解析解,但并不是所有的问题都存在解析解。 解析解可以进行很好的数学分析,但解析解对问题的限制很严格,导致它无法广泛应用在深度学习里。随机梯度下降解析解并不好寻找,我们用到一种名为梯度下降(gradient descent)的方法, 这种方法几乎可以优化所有深度学习模型。 它通过不断地在损失函数递减的方向上更新参数来降低误差。梯度下降最简单的用法是计算损失函数(数据集中所有样本的损失均值) 关于模型参数的导数(在这里也可以称为梯度)。 但实际中的执行可能会非常慢:因为在每一次更新参数之前,我们必须遍历整个数据集。 因此,我们通常会在每次需要计算更新的时候随机抽取一小批样本, 这种变体叫做小批量随机梯度下降。然后用已经学习的线性回归模型就能对目标进行预测。向量化加速在训练我们的模型时,我们经常希望能够同时处理整个小批量的样本(如利用小批量样本实现随机梯度下降)。 为了实现这一点,需要我们对计算进行矢量化, 从而利用线性代数库,而不是在Python中编写开销高昂的for循环。正态分布与平方损失正态分布与线性回归密切相关,本次研究针对于对于噪声分布的假设,服从正态分布的噪声很理想化,均值为0,似然就是对于预测值的接近,最大似然值就是对于预测值的估计反向传播反向传播依靠计算图实现,见深度学习入门鱼书p146理论知识到这里softmax回归前面我们学习了线性回归,线性回归主要用于对于问题的预测,输出一个结果值,但问题往往不止这一种,我们每天也在处理很多分类的问题,要的结果是哪一种。所以本节学习softmax回归模型分类问题对于分类问题,我们要的结果是输出一个类别统计学家很早以前就发明了一种表示分类数据的简单方法:独热编码(one-hot encoding)。 独热编码是一个向量,它的分量和类别一样多。 类别对应的分量设置为1,其他所有分量设置为0。例如(1,0,0,)(0,1,0)(0,0,1)这三个向量分别代表三个类别为了估计所有可能类别的条件概率,我们需要一个有多个输出的模型,每个类别对应一个输出。 为了解决线性模型的分类问题,我们需要和输出一样多的仿射函数(affine function)。 每个输出对应于它自己的仿射函数。 在我们的例子中,由于我们有4个特征和3个可能的输出类别, 我们将需要12个标量来表示权重(带下标的w), 3个标量来表示偏置(带下标的b)。 下面我们为每个输入计算三个未规范化的预测(logit):o1、o2和o3。$$ \begin{split}\begin{aligned} o_1 &= x_1 w_{11} + x_2 w_{12} + x_3 w_{13} + x_4 w_{14} + b_1,\\ o_2 &= x_1 w_{21} + x_2 w_{22} + x_3 w_{23} + x_4 w_{24} + b_2,\\ o_3 &= x_1 w_{31} + x_2 w_{32} + x_3 w_{33} + x_4 w_{34} + b_3. \end{aligned}\end{split} $$与线性回归一样,softmax回归也是一个单层神经网络。运算对于softmax的运算,我们要介绍的是softmax函数$$ \hat{\mathbf{y}} = \mathrm{softmax}(\mathbf{o})\quad \text{其中}\quad \hat{y}_j = \frac{\exp(o_j)}{\sum_k \exp(o_k)} $$这个公式y_hat代表的是正确的概率分布,取幂的目的是为了让数保持非负数,为了确保最终输出的概率值总和为1,我们再让每个求幂后的结果除以它们的总和。$$ \operatorname*{argmax}_j \hat y_j = \operatorname*{argmax}_j o_j. $$如上,我们进行求最大输出值,输出可能最大的概率。尽管softmax是一个非线性函数,但softmax回归的输出仍然由输入特征的仿射变换决定。 因此,softmax回归是一个线性模型(linear model)。损失函数损失函数的确定也十分重要,我们使用极大似然估计法,求对数似然来作为损失函数$$ -\log P(\mathbf{Y} \mid \mathbf{X}) = \sum_{i=1}^n -\log P(\mathbf{y}^{(i)} \mid \mathbf{x}^{(i)}) = \sum_{i=1}^n l(\mathbf{y}^{(i)}, \hat{\mathbf{y}}^{(i)}), $$最后得出损失函数$$ l(\mathbf{y}, \hat{\mathbf{y}}) = - \sum_{j=1}^q y_j \log \hat{y}_j. $$这个损失函数通常被称为交叉熵损失,能够表示出不确定性程度,比较直观的反应分类损失,使用交叉熵还因为它有一个特性是其他损失函数不容易替代的,就是交叉熵更强烈的惩罚错误的输出。如果有非常错误的输出,它的值就会变化很大,反馈很强,并且导数更大。损失函数的导数寻找损失函数的导数是一个很重要的问题$$ \begin{split}\begin{aligned} l(\mathbf{y}, \hat{\mathbf{y}}) &= - \sum_{j=1}^q y_j \log \frac{\exp(o_j)}{\sum_{k=1}^q \exp(o_k)} \\ &= \sum_{j=1}^q y_j \log \sum_{k=1}^q \exp(o_k) - \sum_{j=1}^q y_j o_j\\ &= \log \sum_{k=1}^q \exp(o_k) - \sum_{j=1}^q y_j o_j. \end{aligned}\end{split} $$$$ \partial_{o_j} l(\mathbf{y}, \hat{\mathbf{y}}) = \frac{\exp(o_j)}{\sum_{k=1}^q \exp(o_k)} - y_j = \mathrm{softmax}(\mathbf{o})_j - y_j. $$由此得出该导数,以便于后续计算由于softmax常用于分类,对于图片的分类需要采用图像数据集,在此插入图像分类数据集的相关知识图像分类数据集首先引入数据集所需要的准备工作(代码用课上的)%matplotlib inline import torch import torchvision from torch.utils import data from torchvision import transforms from d2l import torch as d2l d2l.use_svg_display()接下来读取数据集# 通过ToTensor实例将图像数据从PIL类型变换成32位浮点数格式, # 并除以255使得所有像素的数值均在0~1之间 trans = transforms.ToTensor() mnist_train = torchvision.datasets.FashionMNIST( root="../data", train=True, transform=trans, download=True) mnist_test = torchvision.datasets.FashionMNIST( root="../data", train=False, transform=trans, download=True)Fashion-MNIST中包含的10个类别,分别为t-shirt(T恤)、trouser(裤子)、pullover(套衫)、dress(连衣裙)、coat(外套)、sandal(凉鞋)、shirt(衬衫)、sneaker(运动鞋)、bag(包)和ankle boot(短靴)。 以下函数用于在数字标签索引及其文本名称之间进行转换。创建函数可视化样本def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5): #@save """绘制图像列表""" figsize = (num_cols * scale, num_rows * scale) _, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize) axes = axes.flatten() for i, (ax, img) in enumerate(zip(axes, imgs)): if torch.is_tensor(img): # 图片张量 ax.imshow(img.numpy()) else: # PIL图片 ax.imshow(img) ax.axes.get_xaxis().set_visible(False) ax.axes.get_yaxis().set_visible(False) if titles: ax.set_title(titles[i]) return axes为了使我们在读取训练集和测试集时更容易,我们使用内置的数据迭代器,而不是从零开始创建。 回顾一下,在每次迭代中,数据加载器每次都会读取一小批量数据,大小为batch_size。 通过内置数据迭代器,我们可以随机打乱了所有样本,从而无偏见地读取小批量。batch_size = 256 def get_dataloader_workers(): #@save """使用4个进程来读取数据""" return 4 train_iter = data.DataLoader(mnist_train, batch_size, shuffle=True, num_workers=get_dataloader_workers())现在我们定义load_data_fashion_mnist函数,用于获取和读取Fashion-MNIST数据集。 这个函数返回训练集和验证集的数据迭代器。 此外,这个函数还接受一个可选参数resize,用来将图像大小调整为另一种形状。def load_data_fashion_mnist(batch_size, resize=None): #@save """下载Fashion-MNIST数据集,然后将其加载到内存中""" trans = [transforms.ToTensor()] if resize: trans.insert(0, transforms.Resize(resize)) trans = transforms.Compose(trans) mnist_train = torchvision.datasets.FashionMNIST( root="../data", train=True, transform=trans, download=True) mnist_test = torchvision.datasets.FashionMNIST( root="../data", train=False, transform=trans, download=True) return (data.DataLoader(mnist_train, batch_size, shuffle=True, num_workers=get_dataloader_workers()), data.DataLoader(mnist_test, batch_size, shuffle=False, num_workers=get_dataloader_workers()))下面,我们通过指定resize参数来测试load_data_fashion_mnist函数的图像大小调整功能。train_iter, test_iter = load_data_fashion_mnist(32, resize=64) for X, y in train_iter: print(X.shape, X.dtype, y.shape, y.dtype) breaksoftmax从零实现准备工作import torch from IPython import display from d2l import torch as d2l batch_size = 256 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size) num_inputs = 784 num_outputs = 10 W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True) b = torch.zeros(num_outputs, requires_grad=True)实现softmax由三个步骤组成:对每个项求幂(使用exp);对每一行求和(小批量中每个样本是一行),得到每个样本的规范化常数;将每一行除以其规范化常数,确保结果的和为1。softmax函数def softmax(X): X_exp = torch.exp(X) partition = X_exp.sum(1, keepdim=True) return X_exp / partition # 这里应用了广播机制模型定义def net(X): return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)损失函数(一行代码实现交叉熵好牛)def cross_entropy(y_hat, y): return - torch.log(y_hat[range(len(y_hat)), y]) cross_entropy(y_hat, y)分类精度def accuracy(y_hat, y): #@save """计算预测正确的数量""" if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: y_hat = y_hat.argmax(axis=1) cmp = y_hat.type(y.dtype) == y return float(cmp.type(y.dtype).sum())累加类class Accumulator: #@save """在n个变量上累加""" def __init__(self, n): self.data = [0.0] * n def add(self, *args): self.data = [a + float(b) for a, b in zip(self.data, args)] def reset(self): self.data = [0.0] * len(self.data) def __getitem__(self, idx): return self.data[idx]实现def train_epoch_ch3(net, train_iter, loss, updater): #@save """训练模型一个迭代周期(定义见第3章)""" # 将模型设置为训练模式 if isinstance(net, torch.nn.Module): net.train() # 训练损失总和、训练准确度总和、样本数 metric = Accumulator(3) for X, y in train_iter: # 计算梯度并更新参数 y_hat = net(X) l = loss(y_hat, y) if isinstance(updater, torch.optim.Optimizer): # 使用PyTorch内置的优化器和损失函数 updater.zero_grad() l.mean().backward() updater.step() else: # 使用定制的优化器和损失函数 l.sum().backward() updater(X.shape[0]) metric.add(float(l.sum()), accuracy(y_hat, y), y.numel()) # 返回训练损失和训练精度 return metric[0] / metric[2], metric[1] / metric[2]训练函数def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater): #@save """训练模型(定义见第3章)""" animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9], legend=['train loss', 'train acc', 'test acc']) for epoch in range(num_epochs): train_metrics = train_epoch_ch3(net, train_iter, loss, updater) test_acc = evaluate_accuracy(net, test_iter) animator.add(epoch + 1, train_metrics + (test_acc,)) train_loss, train_acc = train_metrics assert train_loss < 0.5, train_loss assert train_acc <= 1 and train_acc > 0.7, train_acc assert test_acc <= 1 and test_acc > 0.7, test_acc小批量随机梯度下降lr = 0.1 def updater(batch_size): return d2l.sgd([W, b], lr, batch_size)简洁实现import torch from torch import nn from d2l import torch as d2l batch_size = 256 train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)初始化# PyTorch不会隐式地调整输入的形状。因此, # 我们在线性层前定义了展平层(flatten),来调整网络输入的形状 net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10)) def init_weights(m): if type(m) == nn.Linear: nn.init.normal_(m.weight, std=0.01) net.apply(init_weights);损失函数loss = nn.CrossEntropyLoss(reduction='none')优化算法trainer = torch.optim.SGD(net.parameters(), lr=0.1)onehot 独热就像手写数字识别中的 0 1 0 0 0 0 0 0 0 0一样,只有正确值赋值1而其他赋值0的方式就是onehot表示多层感知机前面咱们使用过单层的感知机了,多层感知机就是在原有的基础上,加入隐藏层,从而克服线性隐藏层的限制,要做到这一点,最简单的方法是将许多全连接层堆叠在一起。 每一层都输出到上面的层,直到生成最后的输出。 通常缩写为MLP。要实现数值稳定性数值稳定性在深度学习中是十分重要的在进行反向传播求梯度时,根据链式法则,我们知道,梯度计算的结果是有许多的矩阵与一个梯度向量的乘积,会受到数值下溢的影响,引起梯度爆炸或梯度消失,原理很简单,就是过多的概率相乘带来的结果,不稳定的梯度带来的风险很大梯度消失参数更新过小,导致模型无法学习sigmoid函数就是导致梯度消失的常见原因,由于sigmoid函数是饱和函数,在输入很大或很小时其梯度都会消失。导致模型梯度被切断梯度爆炸参数更新过大,破坏了模型的稳定收敛与模型消失相反,但同样让人烦恼,模型爆炸也是一种不可避免的问题对称性神经网络设计中的另一个问题是其参数化所固有的对称性。在这种情况下,我们可以对第一层的权重进行重排列, 并且同样对输出层的权重进行重排列,可以获得相同的函数。在基于梯度的迭代(例如,小批量随机梯度下降)之后, W1的所有元素仍然采用相同的值。 这样的迭代永远不会打破对称性,我们可能永远也无法实现网络的表达能力。 隐藏层的行为就好像只有一个单元。 请注意,虽然小批量随机梯度下降不会打破这种对称性,但暂退法正则化可以。模型初始化在模型训练中,我们想努力使训练更稳定,目标就是要让梯度值在合理的范围内。可使用的方法有:把乘法变加法归一化合理的权重初始和激活函数权重初始化在合理值区间里随机初始参数训练开始的时候更容易有数值不稳定远离最优解的地方损失函数表面可能很复杂最优解附近表面会比较平Xavier初始不能同时满足前一层与后一层的方差=1,采用折中的办法这节,我学不懂,等以后会概率论了再说吧只听懂了一点就是激活函数的由来假设我有一个线性函数等于ax+b,我要实现通过这个函数,让我的均值与方差保持不变,可以求解得到a=1,b=0。由此达到relu函数为什么好用了。检查激活函数使用泰勒展开检查 可以发现各个激活函数的合理性环境与分布偏移模型的数据来源,数据精度是很重要的问题,我们在训练模型的时候一定关注这些问题,当数据分布改变时,模型部署可能会出现灾难性的失败。分布偏移类型
2024年06月02日
14 阅读
0 评论
0 点赞
2024-06-01
torch进阶
Torch进阶损失函数函数方式定义的损失函数易于定义,直接写个函数就ok不过我们也可以以类定义以类方式定义损失函数虽然以函数定义的方式很简单,但是以类方式定义更加常用以类定义损失函数时,我们继承自nn.Module类,将其作为神经网络的一层看待,也可以利用自动求导例如我们要实现Dice Loss(分割领域常见的损失函数)$DSC = \frac{2|X \bigcap Y|}{|X| + ||Y}$我们的实现代码class DiceLoss(nn.Module): def __init__(self,weight): super(DiceLoss,self).__init__() def forward(self,inputs,targets,smooth=1): inputs = F.sigmoid(inputs) inputs = input.view(-1) targets = targets.view(-1) intersection = (inputs*targets).sum() dice = (2.*intersection + smooth)/(inputs.sum() + targets.sum() + smooth) return 1 - dice动态调整学习率学习率的大小会影响模型调优,我们使用scheduler方法来实现动态调整策略pytorch中的lr_scheduler已经提供给了我们动态调整的方法我们可以使用help(torch.optim.lr_scheduler)来查看他们的使用方法使用示例如下# 选择一种优化器 optimizer = torch.optim.Adam(...) # 选择上面提到的一种或多种动态调整学习率的方法 scheduler1 = torch.optim.lr_scheduler.... scheduler2 = torch.optim.lr_scheduler.... ... schedulern = torch.optim.lr_scheduler.... # 进行训练 for epoch in range(100): train(...) validate(...) optimizer.step() # 需要在优化器参数更新之后再动态调整学习率 # scheduler的优化是在每一轮后面进行的 scheduler1.step() ... schedulern.step()我们可以看到scheduler是在每一轮训练结束后使用的,需要放在optimizer后面进行使用模型微调我们在使用参数量比较大的网络时,不能将模型从头到尾再训练一遍例如我们在进行特定的图像分类时可以重写分类层,前面学习到的“知识”不变,所以我们要进行模型微调pretrained参数通过True或者False来决定是否使用预训练好的权重,在默认状态下pretrained = False,意味着我们不使用预训练得到的权重,当pretrained = True,意味着我们将使用在一些数据集上预训练得到的权重。训练特定层在我们只想训练特定层时,经常涉及到部分层的冻结,这就涉及到参数的属性 require_grad ,当这个值取False时,该参数不再更新所以我们指定函数进行层的冻结def set_parameter_requires_grad(model, feature_extracting): if feature_extracting: for param in model.parameters(): param.requires_grad = Falsetimm使用timm.list_model进行模型查看 使用timm.create_model进行模型创建timm模型是torch.model的子类所以我们进行模型保存与提取时使用torch的方法即可半精度训练半精度训练能够减少显存的占用,使得同时加载更多的数据进行计算首先我们需要引入from torch.cuda.amp import autocast模型定义中需要使用修饰器@autocast() def forward(self, x): ... return x在训练过程中,只需在将数据输入模型及其之后的部分放入“with autocast():“即可: for x in train_loader: x = x.cuda() with autocast(): output = model(x)半精度训练主要适用于数据本身的size比较大(比如说3D图像、视频等)数据增强我们遇到过拟合问题的时候加入正则项或者减少模型参数 但是最简单的避免过拟合的方法就是增加数据所以数据增强技术可以提高训练数据集的大小与质量 以便于使用他们构建更好的深度学习模型使用imgaug进行数据增强相比于torchvision.transforms,它提供了更多的数据增强方法,因此在各种竞赛中,人们广泛使用imgaug来对数据进行增强操作。imgaug是一个图像增强库,并未提供IO操作,建议使用imageio进行读入,如果是OpenCV进行读入时,需要将BGR图像手动转化成RGB图像 使用PIL.Image进行读取时,因为读取没有shape属性,需要手动将img转换为np.array的形式再进行处理,官方的例程中野兽使用的imageio进行读取单张图片处理from imgaug import augmenters as iaa # 设置随机数种子 ia.seed(4) # 实例化方法 rotate = iaa.Affine(rotate=(-4,45)) img_aug = rotate(image=img) ia.imshow(img_aug)如图就是从augmenters中使用Affine方法进行的操作,将图片随机旋转-4到45度,以便进行图片的增强有时候我们会对一张图片做多种处理我们使用augmenter.Sequential进行数据增强的pipline例如aug_seq = iaa.Sequential([ iaa.Affine(rotate=(-25,25)), iaa.AdditiveGaussianNoise(scale=(10,60)), iaa.Crop(percent=(0,0.2)) ])对批次图片进行处理实际使用中我们需要进行批次图片的处理操作如下images = [img,img,img,img,] images_aug = rotate(images=images) ia.imshow(np.hstack(images_aug))使用images参数,输入图像列表,就能达批次同效果我们甚至可以使用imgaug进行分部分处理使用iaa.sometimes进行比例划分,使用不同的处理方法可视化网络结构复杂的同时,我们需要一个可视化的工具来方便我们确定输入输出、模型参数使用torchinfo可以做到可视化使用方法非常简单,直接使用torchinfo.summary()就行了,必须的参数是model 与 input_size但你使用的是colab或者jupyter notebook时,想要实现该方法,summary()一定是该单元(即notebook中的cell)的返回值,否则我们就需要使用print(summary(...))来可视化。TensorBoard使用TensorBoard就相当于加入一个记录员,记录我们指定的数据,包括每一层的权重,训练loss等,最后使用网页形式进行可视化from tensorboardX import SummaryWriter writer = SummaryWriter('./runs')我们首先制定了writer作为记录员pytorch中自带的TensorBoard通过以下方法引入from torch.utils.tensorboard import SummaryWriter我们使用像summary中同样的思路,给定输入数据,前向传播得到模型结构,使用TensorBoard进行可视化writer.add_graph(model, input_to_model = torch.rand(1, 3, 224, 224)) writer.close()对于处理图像,我们也可以在TensorBoard中进行可视化展示对于单张图片,使用add_image对于多张图片,使用add_images有时需要将多张图片拼接成一张图片后用writer.add_image进行表示import torchvision from torchvision import datasets, transforms from torch.utils.data import DataLoader transform_train = transforms.Compose( [transforms.ToTensor()]) transform_test = transforms.Compose( [transforms.ToTensor()]) train_data = datasets.CIFAR10(".", train=True, download=True, transform=transform_train) test_data = datasets.CIFAR10(".", train=False, download=True, transform=transform_test) train_loader = DataLoader(train_data, batch_size=64, shuffle=True) test_loader = DataLoader(test_data, batch_size=64) images, labels = next(iter(train_loader)) # 仅查看一张图片 writer = SummaryWriter('./pytorch_tb') writer.add_image('images[0]', images[0]) writer.close() # 将多张图片拼接成一张图片,中间用黑色网格分割 # create grid of images writer = SummaryWriter('./pytorch_tb') img_grid = torchvision.utils.make_grid(images) writer.add_image('image_grid', img_grid) writer.close() # 将多张图片直接写入 writer = SummaryWriter('./pytorch_tb') writer.add_images("images",images,global_step = 0) writer.close()torch生态torchvisiontorchvision.datasets其中包含了我们在计算机视觉领域常见的数据集torchvision.transforms我们知道,数据集中的数据格式或者大小不一样,需要进行归一化与大小缩放等操作,都是常用的数据预处理方法,例如from torchvision import transforms data_transform = transforms.Compose([ transforms.ToPILImage(), # 这一步取决于后续的数据读取方式,如果使用内置数据集则不需要 transforms.Resize(image_size), transforms.ToTensor() ])torchvision.models为了提高训练效率,减少不必要的重复劳动,pytorch提供给我们很多的预训练模型供我们使用torchvision.io里面提供了视频、图片和文件的IO操作等功能,包括读取、写入、编解码处理等操作torchvision.opstorchvision.ops 为我们提供了许多计算机视觉的特定操作,包括但不仅限于NMS,RoIAlign(MASK R-CNN中应用的一种方法),RoIPool(Fast R-CNN中用到的一种方法)。在合适的时间使用可以大大降低我们的工作量,避免重复的造轮子,想看更多的函数介绍可以点击这里进行细致查看。torchvision.utils提供了很多可视化的方法,帮助我们将若干张图片拼接在一起,可视化检测与分割的效果pytorchvideo视频处理 用的时候学torchtext文本处理torchaudio音频处理
2024年06月01日
10 阅读
0 评论
0 点赞