Transcription
那么,我们开始吧。今天,我们将使用一个Python,一个新框架来安装我们之前用NumPy非常辛苦完成的东西。那么,从你们走工程方向的角度来看,你们会觉得,哦,为什么我们突然要去做那些辛苦的事情呢?为什么我们不直接学习这个呢?那么,在普通工程级别,普通工程师可能会觉得,好吧,那时我可能只能做一些初级的工作,但当我达到更高层次时,我需要构建大型系统,我需要更深入地理解,很多时候我需要改变它,以使其适合我的系统响应,所以那时我需要非常深入地理解,理解很多。所以很多时候,以前,研究和工程是分开的,那时这两个方向非常不同。而现在,它根本没有分开。现在,如果你想进入大公司工作,你必须至少有一篇顶级的论文,他们才会接受你进去做普通的工程师,各位。所以,你们可以尝试搜索那些大公司,这里的大公司不仅仅是像韩国的三星、现代或Facebook那样的老牌公司,甚至包括那些真正意义上想要创造产品的初创公司。因为初创公司必须创造产品并与那些大公司竞争。所以那时产品必须达到世界水平,这时他们会招聘最优秀的人才,专门针对那个领域,那个区域。他们的CEO,他们的领导者会绘制一张包含许多组件的地图,然后他们会招聘最优秀的人。世界第一是不可能的,但要成为符合标准、能做好那部分工作的最优秀人才之一,如何才能达到标准呢?那就很简单,只要有顶级的论文就行了。所以,以前在韩国找工作的时候,刚毕业想找工作时,调查发现就是这样,所以研究和工程之间的距离并不像以前那样遥远,所以如果我们只学表面知识,我们是走不远的,即使我们在公司工作也是如此。所以,这就是为什么我必须站稳脚跟,做很多举重和俯卧撑的原因。在使用这个之前,我觉得非常轻松。好的。今天我们来看看它有多轻松。我们来试试。
那么,今天我们将讨论Python,然后将其应用于我们迄今为止所学的知识,包括线性回归、逻辑回归和softmax回归。那么,现在我们来看看PyTorch是怎样的。PyTorch是由Meta内部的一个团队创建的,该团队的负责人是Jen Colon先生,他即将退休。现在,当前的版本就是这个版本。所以你们可以在Windows和Mac上使用它,但通常在研究中,大家更常在这个操作系统上使用它。那么,当你们安装时,不要安装最新的版本,因为很多时候最新的版本,例如选择最新的操作系统,选择最新的CUDA,可能并不总是最好的,各位。我们应该选择稍微旧一点的版本,它们更稳定。因为每个版本发布时都有其差异,很多时候它与旧版本不同。所以很多系统选择了一个CUDA版本,然后他们深入了解那个CUDA,并且喜欢与那个CUDA兼容。所以很多时候我们也不需要选择最高级的那个。
那么,其他同学听得清楚吗?小战听得不太清楚。那么其他同学还好吗?有没有掉线呢,各位?好的。那么小战请检查一下你那边的网络,其他同学。好的。那么PyTorch有什么呢?其实它没什么特别的。我们有NumPy。NumPy是在CPU加上RAM上运行的。而这个设计不同,它不能很好地并行运行,所以我们才有了GPU显卡。它里面包含了内存和处理器。所以NumPy只能在这个上面运行。所以现在如果想在下面运行,让它能够训练一个大型模型并进行大量并行计算,我们就需要学习PyTorch,其实它的语法和NumPy非常相似。如果NumPy也能在这里运行,那谁还会创建PyTorch呢?所以创建PyTorch的原因就是因为NumPy不能运行。所以我们会发现PyTorch和NumPy非常相似。它唯一的区别是NumPy中我们称之为array,而PyTorch中称之为tensor。所以我们将导入一个名为torch的库。Torch就是那个火炬。然后p是这个在Python语言中安装的。它就在这个库里面。然后我们调用tensor,传入一个列表,它会返回给我们一个tensor,对吗?然后,小德也觉得卡顿了,那么可能今天有些网络卡顿了,各位请耐心一点,如果卡顿严重的话,请告诉我。哦,好的好的,我们可以创建一个tensor。Tensor是PyTorch内部的一种数据类型。我们可以传入一个列表,或者传入一个NumPy数组,然后我们调用from_numpy函数。这个函数我们很少用。所以很多时候我们有这个数据就用,但通常我们很少这样用。我们有random函数。这个random函数让它运行从0到1,给你们试试看。首先,我们导入torch。然后我们经常使用这个.rand函数。那么,这个函数它不是数学中我们通常说的那些函数,它会返回一个数字,因为现在我们是在向量上工作,它总是以向量的形式出现,或者说向量,当我们说向量时,我们会想象一个一维数据,对吗?那么在这里,人们称之为tensor,当说到tensor时,它可以是任何维度的。所以当我们创建这个时,我们必须说明维度,例如我们说创建一个2x3的矩阵。所以当我们调用PyTorch中的函数时,我们总是传入一个形状(shape),告诉它这是一个2x3的矩阵,然后它会生成一个2x3的矩阵,每个位置生成一个从0到1的数字。此外,它还有这个函数,你们猜猜这个函数和下面的函数有什么区别,根据你们到模块6为止的编程经验,它有什么不同呢?当然,它会生成负数,但你们猜猜它多了一个字母n是什么意思?下面的函数有字母n。N是negative吗?不是normal,不是normal integer吗?n怎么会是integer呢?你们猜,哦,正态分布(normal distribution),它是normal的缩写,不是normal_lin,normal_lin是不同的,normal_lin是数据,而normal是高斯分布的一种形式,均值为0,标准差为1,所以它被称为normal,正态分布。所以NumPy中也有这个函数,对吗?加上字母n就是normal。所以这个函数我们将用来在softmax任务中做什么步骤呢,各位?例如在softmax任务中,我们将使用这个函数,你们猜猜这个函数会在softmax函数的哪个步骤中使用?也就是说,我们将生成一个随机数,它近似于0,但围绕着0。那么它会在哪个步骤中呢?在softmax任务中,逻辑回归任务中,还是线性回归任务中?这个函数是用来,没错,用来初始化参数,初始化theta的。好的,稍后我们将用它来初始化theta。N,normal distribution,或者这里我们也有range,只是介绍一下,我从来没用过这个,只是介绍一下。所以基本上我们主要使用这个函数,而这是从列表中创建tensor的方法。现在我来尝试添加一个tensor,它会显示这个。打印一个tensor,它会显示这个。或者我们创建一个tensor,torch.tensor,传入一个列表1 2 3。然后我们运行它,它会打印出这个。好的好的。
这种技术叫做广播(broadcasting),和NumPy中一模一样,所以我会讲得很快。我们有一个tensor,我们有另一个tensor,但它们的形状(shape)不同,这时它会生成一个2x2的矩阵,每个元素都是1,然后将这两个相加得到这个结果,对吗?这是一个一维的tensor,对吗?如果这是一个一维的和一个数字,那么如果我们只传入一个数字加上一个普通数字,它仍然可以做到。所以它也和NumPy一样,对吗?我们可以将两个tensor相加,一个tensor加上一个数字,或者两个tensor进行乘除加减。那么,假设我们有一个tensor和一个数字y,y是数字5。那么我们相减,当这样相减时,这个y会变成广播(broadcasting)。那么y会变成值5。一个全是5的向量会变成这样。那是两个向量,两个tensor相减。我们也可以正常操作。所以它唯一的区别在于我们初始化数据的方式。只是初始化tensor的方式。而所有下面的操作都和普通的Python编程一样,并且和NumPy非常相似。所以我们只要基于NumPy就行了。
好的,此外它还为我们准备了一些东西,也就是说,它是一种编程语言,我们完全可以从头到尾进行正常的编程,但是PyTorch是为深度学习而创建的,所以当然会有一些与我们刚刚学过的东西相关的功能,其中有一个叫做均方误差(Mean Squared Error),我们经常使用它,对吧?均方误差就是我们对每对值取平方,然后相除。
这就是均方误差(Mean Squared Error)。其中我们有一个现成的函数,按理说就应该是这样,对吧?就是torch.min_square_error,但它加了“loss”这个词。加上“loss”这个词有它的原因。原因是这是一个损失函数,以后我们将用它来训练。它还添加了这个包,这意味着这个包是用于神经网络(NN是Neural Network)的,用于我们刚刚学过的那些东西,那些相互连接的节点,比如softmax等等,这被称为神经网络(NN),它还添加了层(layer)。Softmax虽然太简单了,但它也是神经网络最简单的形式之一。
我来试试这个。我们创建两个tensor,对吗?我来重新输入这个。得到什么数字?7.5吗?1 2 3 4 555。我来快速复制一下。1 2 3 4,我们称之为x。然后y等于。传入555。然后我们继续声明。上面我们已经声明了一个名为torch的包。NN是神经网络(neural network)的缩写,点min_square_error_loss。那么这里是什么呢?这里是我们创建这个类(class)。你们记得在面向对象编程中,我们创建一个类,然后通过括号来调用这个类,对吗?所以这里我们称之为min_square_error_loss。现在我们才声明这个loss。现在我们才使用它,我们使用它并打开括号。就像我们在这个类中有一个默认函数一样。这个默认函数会接收x和y的值来计算均方误差。那么这里我们有4的平方等于16,加上3的平方,3的平方是9,等于25,加上2的平方和1的平方是5,再加起来是30,对吗?30除以4是7.5,对吗?res等于什么呢?这些错误我们经常遇到,例如这里它说我们经常遇到数据类型的问题,所以我们必须再声明一行。嗯,这段代码。我先试一下,然后再和你们讨论。它这里也有两个意思,它已经先运行了。所以,我还没有更新幻灯片,各位。抱歉,各位。按理说应该先测试再更新这里的幻灯片。因为这是9个月前的事情了。那是上一期课程。上一期课程的版本是这个,哦,不是,你们知道的,我明白了。幻灯片里是.00,这里我没有.00,我来给你们解释一下。刚才我以为是版本变了,但我试了一下,我先确认一下,然后再告诉你们。我确认了一下,好的。刚才我们没有加.0,所以它会把数据类型理解为整数,因此在这个loss函数中,它不接受整数类型,因为它强制要求这种类型必须是浮点数(float),因为它此时要求程序员说,好的,整数类型是用于标签值(label)的类型。所以那时我们必须清楚这一点。而这里接收的值是均方误差函数的值,这个函数接收预测值(y_hat)和标签值(label)。这里是回归问题中的值,所以它不可能是整数。它必须是小数。所以这里我们必须,如果像刚才那样是整数,我们必须强制转换为浮点数,或者我们加上.00,它就会自动识别为浮点数。好的,各位。
好的,Kathy同学问到正态分布,那个只是提前说一下。至于那个,我们两周后的下一个模块会有一个专门关于初始化的课程。那么这个任务就是均方误差。这段代码描述了均方误差。所以它只是一个为我们完成任务的函数,否则我们现在可以把这两个相减,平方,然后正常求和,对吗?只不过它被设计成方便我们更快地编程。这样我们就能更快地构建神经网络。
好的,这里是关于cat函数(concatenate)。Cat函数是用来连接的,其中axis 0是沿着这个方向,axis 1是沿着这个方向。它和NumPy一样,对吗?所以如果我们沿着dim 0连接,它会沿着这个方向。而如果我们沿着dim 1连接,它会沿着这个方向。它就会变成这样。这个函数以后当我们有图像数据,有文本数据,我们通过一个模型,它会生成一个向量,一个tensor,然后图像数据通过一个模型,它会生成一个向量,抱歉,一个tensor才对。然后还有其他各种数据,每种数据都通过一个专家混合模型(Mixture of Experts),各位。那是现在的一个热门趋势。每个都通过一个模型,然后我们把它们连接起来,拼接起来。
好的,这个和NumPy一模一样,我快速说一下。Argmax沿着维度0。所以这两个。这个是max,这个是max。而argmax沿着维度1是沿着这个方向,对吗?所以我们用max函数来解决哪个问题呢,各位?在softmax任务的哪个步骤?在softmax任务的哪个步骤我们会使用max呢?在最后一步。最后一步是计算准确率(accuracy)的步骤,对吗?所以我们会使用max函数。
好的,这个是新的。这个是新的,而且非常有趣。我来和你们一起做。给一个值为2的tensor。那么这里x等于2,对吗?x最初赋值为2。也就是说,我们计算一个点的导数,那么哪个点必须先写出来呢?x = 2。Tensor是数据,在PyTorch中数据是永远不会改变的。数据是我们加载上来的。我们加载数据是因为数据不会改变。我们训练时就加载数据,数据是永远不会改变的。默认是不改变的。所以如果我们想让它改变,我们想让它成为一个变量值,这个不是变量,各位。上面这些tensor不是变量。一旦创建,它就保持不变。如果我们想要一个新的,我们就必须修改,那时就会生成一个新的。而数据本身,内存本身是不会改变的。因为人们理解数据是永远不会改变的。数据只是读取。例如,如果我们从数据库加载数据,我们只是读取,对吗?我们又不会改变数据。假设我们进行归一化,那时它会保存一个新的变量,然后我们只是读取那个变量。所以PyTorch理解所有被称为数据的东西是永远不会改变的。所以如果我们想改变它本身,想让它像一个变量一样改变其内存区域中的值,那么我们必须添加这个。
现在,我们把它看作一个变量,稍后我将更新它。那么,这样做。现在,首先是y = x的平方,对吗?这个x平方后,再乘以3,然后加上2,得到j,j等于这个。那么,计算J对X的导数,对吗?它将等于J对Y的导数乘以Y对X的导数。那么,J对Y的导数是什么?J对Y的导数是3,乘以Y对X的导数是2x。那么,如果x = 2,J对X的导数将等于12,对吗?它将等于12。那么我来试试看它是否等于12。重新输入这个x,然后我们正常操作,对吗?我们有torch.x,x等于x = 3吧。3.0,请你们帮我计算一下,根据我写出的方程,这时它会等于什么?哦,参数很容易忘记。require_grad,然后你们输入看看,当x = 3,方程如下:方程,嗯,我们还是保持和刚才一样吧,y = x的平方,j = 3 * y + 2,请你们计算一下J对X的导数在这个位置是多少。然后我,它有一个叫做J.backward(),它没有显示给我J.backward(),然后X.grad应该等于18,对吗?38,然后等于18,对吗?好的。现在有同学说,我试一下这个,它报错了,这个不行。你们说不相信,对吗?所以,但是这种好奇心非常好,各位,我自己也是这样,很多时候我必须从书本中学习,仍然会好奇,很多时候我必须亲自尝试才能相信,对吗?我们是程序员,所以它必须有这个。好的好的。
那么这个怎么解释呢?就是当我们有X的时候,嗯,这里的解释就是这张图。当我们在这里构建X时,PyTorch会为我们构建这样一棵树。当它构建出这样一棵树时,就像我们人类看到后可以反向计算导数一样,这时PyTorch有了信息,它也能计算导数。所以计算反向传播(backward)时,它会计算每个位置的导数。计算所有位置的导数。这个位置是这里的y位置,对吗?所以它计算y位置的导数。然后这里是J位置。还有很多其他位置,它会计算所有位置的导数。这里我们允许它成为一个变量。所以我们计算导数,试试看能不能打印出y。看看它是否保存了。我猜它不会保存,因为它不需要保存,因为我们没有告诉它这是一个会改变的变量,它为什么要保存呢,对吗?所以这些内部的计算,人们称之为数据,但是我们不会尝试去实现这个。它会在内部实现,它有一个专门处理这个的,哦,tensor是不会改变的。那么我们有这个就太麻烦了。那么我们有一个名为variable的变量,例如。是吗?它能接收一个列表吗?我想我还没试过。我想不行,各位。不,它不接受。Variable也必须接收一个tensor。直接做可以吗?Torch.autograd.variable,导入这个,然后autograd.variable,试试看autograd.variable导入,传入variable,variable也需要grad,那么我们还用variable干什么呢?我再试一下,让我先让它运行起来。然后我再试试看能不能去掉这个。因为这里我们定义了一个variable,但我们仍然,这是第二种方法,各位。第二种方法,在PyTorch中,我再读一下PyTorch。哦,这里有第二种方法。那么当它创建了variable之后,我想可以去掉这个,我来试试看。现在我先让它运行起来。Autograd.variable,像这样导入torch.autograd,对吗?导入variable。然后在这里创建一个variable,对吗?好的,先创建X,我先让它运行起来。然后我再试它。require_grad等于true,对吗?然后require_grad没有这个。嗯。require_grad等于true。是不是打错了什么地方?哦,英语太差了。那么这样做可以运行吗?好的,可以运行,试试别的。啊,它抓不到,对吗?因为这个本身已经可以训练了,再放入variable有什么意义呢?那就是回到这里。我回到这里。回到这里。好的,没错。没有其他办法了。这个不能直接接收一个列表,所以我们必须通过一个tensor。所以这个variable的方法是,很多时候我们已经有一个tensor,而这个tensor之前仍然是数据,但现在我们想改变它。明白了。好的,我再解释一下。嗯,这里通常我们不能改变,不能计算导数。当我们加上这个时,这时tensor内部有很多东西。那么它内部有那个变量,那个参数,这个类的属性。当我们将这个设置为true时,它就会被使用。那么此外,这是什么?这是我们从一开始就确定x将是一个用于计算导数的变量。但是假设在我们的工作过程中,我们确定,嗯,现在上面已经有很多tensor了,正在同时运行,现在我们确定某个tensor作为变量,那么我们就使用variable,PyTorch也支持我们这样做。除了常规方法之外,我来分析一下这个例子。在这个例子中,我们有4个x,也就是4个输入特征,我们将这四个相加。然后这四个相加得到y。y等于x和y相加。然后y = y的平方。那么当我们计算时,每个位置的导数都等于8,对吗?因为J对Y的导数乘以Y对X的导数,那么它将等于J对Y的导数。
就等于 2y,x 就等于 1。所以这里它会等于 2y。2y 现在在 x = 1 的位置。x = 1 就推导出 y 等于 4,对吗?y 等于 4,所以 2y 相乘就等于 8。所有位置的导数都等于 8。
然后 Võ Hương 问,那么 variable 有很多变量 x variable 是一种将张量转换为一个可以计算导数的方式。再说一遍。
当我们创建一个张量时,首先我们要理解“张量”这个词。张量是多维数据,它可以是任何维度。这里放数字 2,只是为了让它等于一个数字。张量这里它包含一个数字,它就是一个数字,但现在我们这个张量可以创建任意多的东西,对吗?这个张量我们可以创建任何类型的数据。这也是一个张量。或者这里它创建了包含多个数字的张量,或者现在我们创建一个 100x100 的张量也可以。所以,这是一个张量,当我们说到“张量”这个词时,我们理解它就是多维的,任何维度都可以。而且这个张量的所有位置都可以改变,所以它可以计算导数。这是第一种方法。我们从一开始就创建张量,并确定它可以改变。
而这种方法是,某个地方已经有张量了,现在我们希望它改变,那么我们就再嵌套一个这个。那么这两个步骤就等于一个步骤,就是这个步骤。它支持很多种方式。
当我们有了这个,就可以计算导数。稍后我们有一个变量 di,我们调用 backward 函数。那么在这个 X 里面,它有一个属性叫做 .grad,它包含梯度。这样构建它就有了。目前正在使用最新的 PyTorch。
看看版本是多少,兄弟们。看看,嗯,肯定不是最新的,但它也算是新的。torch.get_version 2.3 版本以下是新的吗?在这里看看,这里是 2.9。所以它也稍微有点差距,但影响不大,因为通常从 1.6 开始就饱和了,所以现在它只是添加了一些小东西,添加了一些额外的功能。核心部分已经饱和了。
所以这里我刚才用一个变量演示,现在我用四个可以改变的变量演示,而不是说 variable 就有更多变量,不是的。之后我们这里有几个步骤。稍后我将应用于线性回归问题来解释。这里只介绍函数名。稍后我们将直接应用并查看数字,这样更容易。因为线性回归问题我们已经很了解了,我们会看得更清楚。
我们经常做这样的操作:theta 等于 theta 减去 learning_rate 乘以 loss 的梯度。如果这是一个数字 n 向量,向量也可以。向量就是这样根据 loss 的,而且这个 learning_rate 等于 0.1,那么这个操作就等于这一行。而且它应用于变量 x,那时怎么办?
我来试着应用一下。声明一下:这里我们声明一个库是 torch.optim。Optim 就是 optimization。然后是 .sgd。optimization 等于 .optim optim 不知道为什么这里没有 .sgd。
在 SGD 中,我们分析一下,SGD 会接收一个列表,一个包含那些具有 require_grad 等于 true 属性的张量或变量的列表。variable 就是这个 require_grad 等于 true,或者说是附加了 require_grad 等于 true 的张量,它必须是这种形式。我们把它放在里面,有时候我们有很多变量。我们有变量 x1, x2, x3,抱歉,我们有张量 1, 张量 2, 张量 3,它们是独立的。那时我们把它们组合成一个列表。而在这里我们只有一个变量。这不是很多。这只是一个张量。我们也把它放进去。我们设置学习率是多少呢?X 学习率等于 0.1。我再运行一下上面这个。返回。
这个等于在这个位置的梯度是 18。如果等于 0.1,那么此时我们要做什么?我拿这个地方。这是一个方法,theta 减去 theta t。抱歉。Theta 等于 theta 减去 learning_rate 乘以梯度。它把这个 X 赋值。那么它就理解为 theta 现在就是这个 x,对吗?Theta 就是这个。它将以 learning_rate 等于 0.1 的学习率,沿着梯度的反方向更新这个值。我们怎么做这件事呢?.step。打印 X。再试一下看看对不对。就是 x 现在等于 3,减去 1.8,是不是等于 1.2 呢,各位?明白吗?到这里明白吗?所以就是这样。
这样就完成了。那么所有事情我们都用这个来完成我们的三个问题。我们学过的三个问题。看来它已经全部支持我们了。看来现在我们来试一下吧。就是它能为我们解决 Softmax 具体问题中的什么事情呢,各位?就是有了这个,我们在哪个步骤会轻松很多呢?我们将在 Softmax 问题中的哪个步骤会非常轻松呢?例如,这里会写出来,在 Softmax 问题中,我们有计算梯度的步骤。对的。
还有什么步骤呢?还有什么步骤它也支持我们呢?计算梯度的步骤是最轻松的,对吗?计算梯度的步骤是最麻烦的,而我们在这里只需要调用 backward 函数。至于 step 函数,它支持我们更新 theta,对吗?我们就一直调用 step,对吗?更新 theta。那么我们已经轻松了两个步骤。最后我们只剩下这个步骤,叫做模型构建步骤。模型步骤如何运行,我们必须自己做。所以我们试着用这些知识来应用于线性回归问题看看吧。
好了,看看还有没有别的。这些都是些细枝末节。它有很多种做法。那么各位可以看看它有很多种做法,我们选择记住一种就好。不要记太多。即使是这些,我也记不住。很多时候,当我在教的时候,必须阅读全面的内容才能讲得全面。但当我实际操作时,记住一个就行了。线性回归。
那么在解决线性回归、逻辑回归和 Softmax 回归这三个问题时,在解决过程中会添加新的 Python 知识。各位,我们边解决边添加新知识。否则,如果之前不列举不说明,一次性讲完 Python,之后我们应用时就都忘了,记不住了。所以现在是讲到哪里就说到哪里,这样更容易记住。那么刚才我们学了什么呢?我们看看我们学了什么,我们学了声明上面的 optim。
刚才我们有这个,对吗?导入,我们看看我们已经有什么了。导入这个。那么刚才我们学到的是我们声明了一个 torch 包。然后在这个里面我们只有一个 variable。忽略这个,它太冗长了。我现在把它总结一下,把重要的、我们需要知道的浓缩起来,以便我们更容易地转向新的内容。
创建一个张量的方法,以及创建一个可以将其转换为 theta 的张量的方法。那么 x 现在就是 theta。之后我们构建这个就很正常了。然后构建完成后,我们取最终值。这个值对应着后面的 loss 值。我们调用 backward 函数,它就会反向传播。Backward 是反向传播到那些 require_grad 等于 true 的变量。那么那个变量的 grad 参数就会保存梯度值。
当我们调用完这个之后,我们声明一个 SGD (Stochastic Gradient Descent),这正是我们一直以来所做的方式。我们调用 start 函数,它就会更新 theta 等于 theta 减去 learning_rate 乘以梯度。这样就非常简洁了。所以现在我们再学一些东西来构建模型。现在只剩下模型部分了。好的。好的。
我来写代码了。我来写代码了,朋友们。所以现在我们慢慢写代码。别担心,我会慢慢写代码。或者哪里不清楚就告诉我,我会写代码。我再介绍一个,就是当我们有节点 x1, x2,然后这里是 j2,那么我们连接这个,连接这个,连接这个,连接这个。各位,这总共有多少个参数?如果看这里,它有多少个参数?那么这个叫做全连接层 (fully connected layer)。这是书本上的叫法。在 TensorFlow 中它叫做密集层 (dense layer)。在 PyTorch 中它叫做线性层 (linear layer)。它们都表达了同样的事情,就是我们有一个数据,叫做数据向量 X,我们有一个向量 J。这两个向量里面有许多小小的元素,它们彼此连接,完全连接在一起。这叫做 fully connected,就是完全连接在一起。
那么现在在线性回归问题中,对于这个问题,例如我们有更多的特征,我们有 x1 x2,然后我们在这里生成一个 j 值,我们只有一个 j,就是 y_hat,那么我们全部连接起来,也是完全连接,所以它也是一个 fully connecting。所以回到这里,这个问题我们只有一个节点,这个问题超级简单。我们有一个节点,然后我们生成一个节点 J。这个叫做 add,可以叫做输入层 (input layer)。Input 还是不是 input layer,叫做输入数量。这个是输出,是输出中的节点数量。
那么这里节点数量是 1,节点数量是 1。所以对于这个问题,各位看看这里有多少个变量?在这个简单的线性回归问题中,我们有多少个变量?在只有一个输入特征的线性回归问题中,我们有多少个变量?两个变量,对吗?W 和 B。
好的,我们来看看。import torch.nn as nn。NN 就是 network。之后我们用 nn.linear。这个 Linear 接收一个输入节点,一个输出节点,我称之为线性层。所以这里我们有这个数字是输入节点数。输入节点就是这个。这个是输出节点数量。各位注意一下。
好的,各位应该拿幻灯片。这是我上传的幻灯片版本2。各位记得拿最新的幻灯片。这部分只是用来调试的,但我会和大家一起做,所以有时候这里的函数你们不记得也没关系。因为当我们真正做的时候,代码会非常简洁。但这里我们调试是为了更好地理解 PyTorch。各位,我们理解得越深越好。你们的薪水将取决于你们的理解程度。所以各位尽管自信,有时候为了让程序运行,我们只需要记住那一部分。但为什么我们要学这么多呢?总的来说,各位,因为我们知道得越多,我们就会越扎实。所以有时候我们要为此做好准备,不要给自己设定一个默认值说我们排斥这件事。所以我们要把这看作是我们的思维也会随之而动。所以我们要为学习新知识做好准备。
现在我来打印一下。打印什么?这里面有一个参数叫做 weight,还有一个参数叫做 bias。我猜 weight 有 s,因为它会有很多个 weight bias weight 没有 weight 或者没有 s 吗?试试看。哦,没有 S。好的。那么这里有两个参数。这是 weight,我们有一个参数。它就是一个 require_grad 等于 true 的张量,它就是 theta。它就是定义变量,可以吗?而这个是 bias。
那么各位注意,这里这个是列向量。这是向量形式,这是两个列向量,就像一个一行一列的矩阵,而这是一个只有一个数字的 bias 向量。那么各位注意,当我们有 x1 x2,然后这里是 0,这里是 1,有 j,那么这里我们有 w1 w2 和 b。那么在这里面,在 weight 中,它会以 W1, W2 的形式存储。而 bias 总是只有一个数字 B。
好的,然后我们接下来做什么呢?之后,假设现在有一个新的问题,这里是数字 4,各位猜猜我们有多少个参数?各位,如果这样做,你们快速输入,那么我们有多少个参数呢?各位注意。这里的数字 4 是四个特征,包括数字 1,因为数字 1 是 PyTorch 可以自己处理的。它替我们做了。PyTorch 被创建出来就是为了支持我们,所以它必须能够自己处理。所以在这里我们看到我们总共有 1 2 3 4 5 个变量,对吗?4 + 1 = 5。好的,我在这里停一下,稍后我会讲更复杂的内容。好的,你先停在那里。
然后另一个扩展部分是,当我们在这里创建一个线性层时,它会自动为我们生成参数。它以某种方式自动生成,我们将在下一个模块中查看这种方式是什么。而在这里,我们理解为每次运行它都会为我们生成不同的参数,不同的 theta 值。通常情况下,没有人会赋值。但现在假设我们想给它赋值。我想给它赋值是为了什么呢?为了测试,对吗?那么此时我们可以这样做。因为这些东西当我们打印出来时,我们看到它们是张量。所以现在我们添加另一个属性叫做 data。那么它将允许我们赋值,.data 就是取出那个张量。取出 bias 本身的张量。我们将其赋值为一个张量,一个某个值。这段代码是我为各位演示的代码,以便稍后我们更容易测试。至于这部分,各位不要试图记住这些。因为幻灯片上有,我们观察一下就可以了。
PyTorch 回到哪里?以后在做线性回归问题时,各位只需要记住这段代码就可以了。其中 linear 等于什么?Linear 等于这个。各位记住这一行。各位记住这一行,然后记住这一行,问题就解决了。线性回归问题就只有这些。但我正在补充说明,让各位看到,所以各位要知道,哪些是补充说明,有时候如果太复杂了,我们就观察一下。
好的,假设参数等于 1.0。.data 等于 2.0。那么此时参数就改变了。也就是说现在我们赋值,赋值是为了什么?为了稍后我们测试一下,看看我们是否真的理解 PyTorch,对吗?所以我们现在来试着做这个问题。哪个问题有数字的。这个问题有数字,对吗?现在我们来做这个问题,看看我们是否真的理解 PyTorch。各位根据刚才我们学到的知识来帮助我。很多时候我也记不住。我会和大家一起做。
首先,我们有数据 6.7 和标签 9.1。好的,我们现在来试着做这个,就知道我们是否理解了。W = 0.34,我们把它放在这里,减去 0.34,b = 0.04 0.04。那么现在我们已经有一个线性层了,对吗?我们有一个线性层。这个 Linear 是什么意思?这个 Linear 意味着我们有一个输入节点和一个输出节点。也就是说输入只是一个数字,输出也是一个数字。它是做什么的?它是张量。现在我来给各位演示一下。好的,我给各位演示一下,这个没问题了,对吗?我把这部分删掉。
创建 X 值等于一个张量,.tensor 它是数据。所以这里传入的数据是什么呢?6.7 6.7,然后我们生成 ih 值等于线性层传入 X,看看它能不能运行。H 传入 X,然后这是什么?矩阵 M 2,它必须是一个矩阵。哦,好的,它有问题。我回到这里,我复制这个,这个问题也很有趣,各位帮我观察一下,这是我们打印出来的结果,这是正确的,这是一个线性层,这是线性层的权重和偏置的组织方式,现在我用这种方式重新赋值,它就报错了。为什么呢,各位?各位帮我看看这一行,为什么会这样?
好的,一个 2D,对吗?所以这里我们必须看看它的属性,它的结构是什么。我们必须遵守,不能自己随意创建,对吗?所以,好的,回答 Minh Hoàng 同学的问题,它们是一样的,有两种方式,所以就是这样。这个没问题了,对吗?运行这个。这是我们打印出来的吗?好的,现在我们赋值这个,然后运行。-2.2,各位觉得熟悉吗?我们边做边调试。因为也希望它有错误,这样各位自己做的时候,我们也能通过错误来学习。
好的,6.7 就会得到 -2.23。我们有 -2.23。那么我们已经计算出 y_hat 了。现在我们计算 loss。这个 loss 我们用均方误差 (mean square error),对吗?网络均方误差。好的,我们继续做 loss function。等于 .N 各位有什么问题尽管问。因为这是新的,尽管问,这样我们能理解得更深。均方误差损失。我们必须声明它。我们先创建类,然后才能使用。之后我们才能在这里使用,这里我们需要有 Y,对吗?把 Y 在下面也创建出来,让它更简洁。Y 等于 记住是 9.1。这个问题我们做了很多次,9.1,对吗?9.1。均方误差会得到 loss 值。Loss function 传入 ih 和 y。打印 loss。哦,这个数字也很熟悉,128.5,那么就没问题了。
128.5,然后我再添加这个幻灯片,这样我们就有梯度值了。梯度值将等于 -151 和 -22,对吗?我们来看看,现在计算梯度,我们调用什么呢?忘了。现在计算梯度,各位帮我输入一下,现在计算梯度的步骤是我们要用哪个变量 .backward?我们就取最后一个,是什么呢,各位?我们怎么计算梯度呢?这里我们取什么?我们 .backward 这里。好的,计算梯度吧。各位帮我输入。loss,对吗?Loss .backward,这样它就计算梯度了。它为我们计算了梯度,然后我们更新。
当我们更新时,我们必须声明 optim,对吗?我到上面复制一下,快一点。哪里哪里?这里。这里是计算梯度。这里更新,分开。好的,现在计算梯度。先计算梯度。之后计算梯度,我们必须知道,我再打印出来。哦,打印什么呢?刚才我记得,各位记得我们有 x.grad,对吗?是一个变量,它会有 grad。那么现在我们取出 x。这个 X 是什么?X 包含权重和 bias。权重 bias 在哪里?它在刚才我们从上面取的那个 linear 里面。看。所以这是一个,这是 x。这是一个变量 x,所以这里我们赋值为 X,它得到 -151 和 -22,对吗?
好的,当我们计算完梯度,对的,-151 和 -22。之后我们计算,就有了新数字。这里的新数字是 0.26 和 117,对吗?学习率等于 0.01。好的。学习率等于 0.01。之后我们更新,我们传入一个列表。哦,这部分我再记住一种做法,就是返回这个线性层中的参数,它有一种做法是 .parameter,我猜有 s,试试看,没有这个方法 .parameter。这里有说什么吗?看看 parameter parameters,对吗?Parameters。对的。好的,看看有什么错误。Method object is not variable。缺少什么吗?哦,缺少什么。哦,它不是一个属性,而是一个函数。好的。函数它返回给我们一个列表。它会收集一个包含权重和偏置的张量列表。
嗯,现在我试试打印这个看看行不行。试试看。试试看,它可能会输出一些东西,它输出的是一个 generator 类型,我们可以理解为它是一个列表,其中包含权重和偏置。好的,当我们打印完这个之后,我们运行这个,然后我们会再次打印这两个的值,看看它们是多少,对吗?就这个。这里。R bias 好了。0.26 和 117 是 0.26 117。看。
好的,各位明白吗?那么我们已经完成了线性回归问题了。我最后把代码整理一下,各位会看到代码是这样的:首先保留这个,然后计算 loss 什么的,这些我们只是额外打印出来,对吗?计算 backward,那么这里假设我们不使用这个参数,它就是这样,那么这句话就是这个就是模型。我们理解它就是模型,因为我们的模型很简单,就只有这样。
然后这里我们加载 X 数据,计算 y_hat,然后我们计算 loss。当我们加载样本时,它会是这样。我们加载样本时,它会是这样,对吗?我们有 x 有 Y。之后我们第一步是加载数据,计算 y_hat,计算 loss,然后计算梯度并更新。当然,这些声明我们不能让它这样声明。我们在最上面的模型部分声明。因为之后它会运行循环。所以循环包括取出数据,计算 y_hat,计算 loss,更新,以及计算梯度并更新。我们就这么简洁。
好的,那么线性回归问题就完成了。现在我们想进入 for 循环,我们就运行 for 循环。稍后我将和各位一起做。现在假设我想再运行一次,是吗?那怎么办?想再运行一次,让它出结果,对吗?假设这里它再次运行这里。我把这个复制到这里。好的,运行,那么这里它输出 117 和 0.26,是这里的新变量,对吗?之后现在想再运行一次,看看 loss 是否会降到 0.86。那么再运行一次,我怎么运行呢?
那么此时计算 ih。选择 ih 并计算 loss,打印这个。打印这个,对吗?这里我们计算 ih,然后计算 loss。其中 y 和 x 已经有了。那么我的新 loss 等于 0.86。这是 8.1 和 0.86。8.1 和 0.86,这个数字是从 numpy 生成的,各位。它们是两个独立的,但我基于 numpy 作为标准,作为正确答案,然后我用这个正确答案去测试 PyTorch 是否能得到相同的结果,以便我是否真的理解 PyTorch。目标是这样,这些数字不是从 PyTorch 生成的,它们是从 numpy 生成的,那时将基于普通代码和手动计算。所以所有一切都基于正确的基础。我们从正确的东西开始构建。
好的,那么在线性回归问题中我们学到了什么?我看看这里有什么新的,我们学到了学习方式。但各位不要记住,你们不需要记住这部分。这是我们可以直接将 theta 赋值为一个预设数字的方式。我们可以这样做,但这种操作很少有人做。
什么时候我们在这里测试,我们就测试。然后在这里我们生成一个线性层。这个线性层它的输入就是一个数字,它就是值x。输出是一个节点,它就是y帽。所以这里是y帽等于x乘以w加b。x乘以这个w加b。就是这样。
然后我们计算损失时,我们使用均方误差。就是这样。那么这就是我们计算损失并声明它的方式。然后我们把y放入损失函数,我们有y帽和y。就是这样。
然后我们计算导数时,我们使用一行损失。反向传播会为所有现在不再是张量requires_grad为True的位置计算导数,而是那些线性层,那些线性层。然后线性层内部的那些东西会有导数。然后我们更新。
更新时我们说,好的,更新什么?可能有很多地方有导数,但我们只能更新几个地方。那么我们通过假设我们想更新这个线性层中的参数,来将它们放入那个列表。那么我们就取那个线性层,调用它的parameters函数。然后我们嗯嗯,再添加这一行。Step就是我们更新一次。
然后然后现在我们再做一点。我们做逻辑回归的练习。但是大家对这里有什么问题吗?大家想让我再解释一下这里面的某个部分吗?大家觉得可以吗?如果可以的话,我们就转到逻辑回归的练习,我们还会有一个新的部分,那就是逻辑回归是分类问题而不是回归问题。所以我们需要使用sigmoid函数,然后我们需要使用交叉熵函数。但是首先我们这里可以吗?如果不行,我们去到那个部分会觉得更难,因为我们还没有清楚理解这个。
大家看看我有没有问上面哪一行。好的。Zero CR吗?好的,zero CR是当我们循环,我们使用for循环进行训练时,那时我们会在这里使用zero CR。然后我会按照幻灯片这里说。我们看看这里有什么想法?我会再说一遍这里是我们进行设置。
然后我们声明损失,然后声明优化器。那么这里的第三项是我们有模型。必须构建模型,然后计算损失。那个模型。这是构建模型的模型。我们的模型只有一个线性层。然后计算L。就是这样。然后我们会计算导数,然后我们更新。
那么当我们计算导数时,假设我们有三个样本,那么此时导数的值是不是等于损失对W的导数的值,它是不是等于三个样本的平均值呢?1/3乘以对每个第i个样本的损失对W的导数之和,我们有三个样本。对吗?大家还记得吗,当我们有多个样本时,我们是不是有一个累加变量,然后我们除以3来计算平均值。就是这样。
那么当我们用三个样本训练完之后,稍后我们也会用三个新样本训练。稍后我们也会用三个新样本训练。那么假设在我们的代码中,在我们的for循环代码中,进入for循环,进入for循环。这里我们是针对epoch进行训练,那个数量,然后这里我们是针对batch进行训练,当我们针对batch size进行训练时,这里面我们会计算每个样本的导数。那么外面我们需要什么变量呢?我们需要什么变量,以便稍后当我们运行完所有batch size后,我们会更新一次呢?在计算m个样本的导数时。大家还记得M样本等于2的那个例子吗?那么这里我们需要声明什么变量?什么变量?例如我们有两个变量W和B。那么这里我们会有什么变量,大家还记得代码5中吗?在代码5中,那里有两个变量。看看它有什么变量。
这里大家就打出来吧。进一步解释一下。我们计算每个样本的导数,然后计算完成。然后到这一段,我们会更新。但是在更新之前,我们必须计算一些东西,这里我们必须计算平均值。计算平均梯度。那么为了计算平均值,这里我们需要什么变量呢?变量s,对了。W的梯度之和和B的梯度之和。那么当我们计算总和时,假设它运行了很多次循环。它运行了很多次循环,对吗?
假设我们这里有batch size,我们运行完所有batch size,然后我们这里的batch size是,例如我们有100个样本,我们只运行batch size为10。那么对于每个batch。对于每个batch吗?等等,对于每个batch。难怪难怪我问大家时大家会犹豫,问错了。对于每个batch,我们才计算这个。好的。那么大家的编码水平有时甚至比我还要扎实。啊,这里才是这里才是计算每个导数,我们进入for循环,for x,对吗?这是对于所有,我们取出所有的batch size,然后对于每一个batch size,我们才计算这个。那么它的默认值必须是0。可以是其他值吗?怎么能是其他值呢?因为我们计算累加和,所以一开始必须是0,对吗?那么在正常情况下。所以它的意义就是这样。
也就是说,假设它运行完一个循环,那么它运行完我这里有两个样本,m样本等于2,运行完2个,然后这个会在里面累加一些东西。里面它会累加,然后下来这里计算平均值,然后它会用一个batch size为10的批次更新一次。那么然后它会循环到这里,运行另外10个样本。当运行另外10个样本时,这个值必须是0才行。因为因为怎么能行呢,对吗?因为是10个新样本啊。所以我们必须执行这个操作。
那么为什么我们要明确地执行这个操作,而Python不帮我们做呢?因为因为在深度学习中,有一些算法它需要训练多次才更新一次。总的来说,它是为了让这个框架的灵活性更好。所以这里它不会自动清除,而是让我们主动清除,以便将来我们可以做更多样化的事情。而Python不仅仅是为了最大回归而生,它还为了很多其他事情。所以这里我们必须手动赋值。所以我们需要这个操作。
好的,没关系。所以我们需要第三个。刚才还没打算说第三个,因为要等到我们说到for循环的时候。但是你正好问了,我就直接说了。那么最后我们有这个,假设我刚才用一个样本训练,现在用多个样本训练也是一样的。在Python中,我们训练多个样本和一个样本没有区别,刚才我们的X数据是,我的X在哪里?我这里就是这样放的,对吗?它是一个样本,现在再添加一个样本,看看此时会怎么想,每个样本都变成一行,对吗?现在再训练一个样本。这个样本是什么?等于4.6 5 5.9 4.6,然后y = 5.9。那么此时我们必须查看形状,检查形状是否正常。因为这个是我们自己设置的。而当我直接检查时,我会和大家一起检查。然后看看它能不能运行。现在有一个样本,现在我创建两个样本看看怎么样。
那个不重要。这是它打印出来的注释。那么两个样本它打印出来的是刚才的两个数字是-2.2和这个是-1.5,这边没有,对吗?就是这样。然后就是这个形状,这里我打印出形状给大家看。点shape 21。那么我们有形状是21。
然后接下来我们计算损失。那么当我们自己这样编码时。那么此时我们必须控制形状。因为我们的输入是21,那么我们的Y i也必须相应地是21。所以这里我点reshape 21,只是为了让它们一致。那么我们的损失就是这样,对吗?就是这样。
然后计算反向传播。计算反向传播,然后计算什么,它就正常进行,对吗?那么当我们有一个样本时,我不会注意形状,因为它只是一个数字,它会进行广播。当有多个样本时,我们不注意形状也可以,它也会进行广播,但我不想让它自动广播,我想自己做。那么此时,改变这里的形状,让它和这里的21一致,以便我们让它们匹配。
那么最后这里是刚刚给大家试了两个样本。那么这里我们一次做四个样本。四个样本一次,对吗?这里我们有x。X包含整个这一列。Y包含整个这一列。就是这样。我标记1,这样它就有两个维度。这个它有两个维度,它就是一个4x1的矩阵。这是一个向量。这是一个4维向量。然后经过这里,它会返回给我们输入。我们的输入X。X是4x1。四个样本。每个样本有一个数字。就是这样。因为这里我们只有一个数字。一个特征而已。所以是四个特征,四个样本和一个特征,4x1。4x1放入这里。这是一个形状为4 1的张量返回给我们。就是这样。
那么每个线性数字,每当你们设计线性层时,它只关心一个样本。在设计方面,我们总是认为我们正在为一个样本运行。那么一个样本只有一个数字。那么一个数字输入,一个数字输出。就是这样。那么我们有四个样本,我们就会有四个样本对应的输出。那么对于回归问题,每个样本的输出只有一个数字。所以这里它会输出41。就是这样。
然后这里我们匹配,我们匹配两个,匹配几个形状。就是这样。然后因为我们会使用for循环运行多次,所以在计算导数之前,我们记得将总和设为0。然后这个只是我们额外做的。这是一个与技术无关,也与知识无关的步骤。它只是一种做法,对应于我们在编程时将其设为零,或者这是TensorFlow或Python要求我们做的做法,它不包含任何深奥的知识。我们只要在计算导数之前,记得按照这样的三步方式进行,计算导数和更新。那么这里我们传递了所有的n个样本。
然后当我们想用一个样本进行训练时,这里我们只是切分出来,没有任何改变,它保持原样。没有任何改变。好的,现在我将为逻辑回归问题一步步地做。在逻辑回归问题中,我们将从...开始。逻辑回归,我们关注这张图。那么我们有两个新知识,那就是sigmoid函数和一个名为二元交叉熵的损失函数。那么我们有sigmoid函数,全部都是sigmoid。而这里在网络中我们有二元交叉熵损失。那么现在我将为你们尝试这两个。sigmoid函数,对吗?
那么与其我为sigmoid单独创建一个例子,现在直接用这个例子可以吗?就用这个例子来做给大家看。那么为了准备一个小的函数在这里,必须是损失函数。那么我们有,我们先用sigmoid函数进行二元测试。Sigmoid是我们有.sigmoid,对吗?就是这样。然后现在创建一个x,这里叫做sigmoid层,它是一个层或一个函数都可以。以后它可以单独成为一个层X,0.0和0点,以及1.0,看看等于多少?AD也可以等于XO除以X,嗯,t.sigmoid,对吗?T.sigmoid sigmoid,啊,这里我们必须.sigmoid,检查一下,这里已经理解了,但我检查一下nn中是否有,以便我们统一,让大家在这里不会困惑。试试看有没有。
那么它有。好的,现在我要说的是,目前在幻灯片中,它有这个,在幻灯片中是这个。在幻灯片中给大家举个例子,我们有class sigmoid,我们直接用,就像函数一样。那么这里我们有set sigmoid,我们有set sigmoid,我们直接传入。啊,这个是小写函数了,那么它有什么区别呢?这里只是一个函数,因为sigmoid不包含参数,所以没有太大区别。但是这里是一个层,那么我认为会更多地使用这个,因为它在深度学习中是一个层,所以我们就把它理解为一个层。那么本质上它的运行方式是相同的。如果这里是一个层,那么我们就声明然后像刚才我们使用的方式一样使用。而这里是一个现成的函数。那么现在这样做,大家会觉得困惑,突然一会儿是层,一会儿是函数。所以我将使用层的方式。那么通常那些外部函数,它内部都有一个对应的层。为什么叫层?因为它在神经网络中,所以叫层。就是这样。
那么这个它返回给我的是0,那么等于0.5,对吗?就是这样。1它等于某个值,但是0它会输出0.5。好的,这是sigmoid。那么稍后我们会使用这个sigmoid。那么如果我们使用函数,我们就用小写字母s。
然后此外我们还有二元交叉熵。那么在二元交叉熵中,它会接收y帽。大家注意,在sigmoid的二元交叉熵中,通常我们会理解这里我们有四个样本。第一个样本返回y帽1,抱歉,第一个样本的y帽,然后第一个样本的标签,然后第二个样本的y帽和第二个样本的标签。就像这样。那么此时我们计算,那么它会输出总的损失值。现在单独计算一下,只计算一个。例如我们0.4到1,它们相距很远,看看会输出什么。它输出一个值0.9。例如我们给0.1,它应该更大,对吗?那么它输出2.3。就是这样。在这里假设我们给0.9,那么会输出一个非常小的数字0.1。那么这就是二元交叉熵函数。它就像我们一直在做的函数一样。负log y帽。那么它在这里会为我们计算的是负log 0.9。就是这样。
现在我们基于两个新东西。第一个是损失函数二元交叉熵。第二个是sigmoid函数,用于实现逻辑回归。好的,这是我将一步步实现的最终代码,以便大家更清楚地理解。好的,这里,这只是一个图。我们有两个参数。而在逻辑回归问题中,嗯,我们仍然有x,我们将其连接到j,然后j经过sigmoid,它就会输出y帽。那么这里如果我们有很多x,那么都会连接到j。所以这是一个线性层。那么,例如这里的线性层。输入是3,输出是1。那么对于这个简单的例子,输入是1,输出是1。就是这样。
然后我们有这个。好的,现在我们来试试。已经准备好这个参数了。我会复制。这次我将用两个样本和大家一起编码。这是第一个样本,这是第二个样本。刚才我添加了一个样本,现在用两个样本看看有什么不同,以便大家看到全貌。好的,为了确保,我只复制。只复制真正需要的。这个文件我也上传到云盘了。大家可以在上面获取。
好的,逻辑回归问题,在这个问题中。这个问题我们分析,这个值1对应B,对吗?那么1是乘以B。这是参数B。这是W1。这是W2。那么,这是x1,这是x2。那么,如果我们创建线性层,它将是linear 21。那么,因为这个问题我们有两个特征,对吗?就是这样。好的,有两个特征。所以现在创建一个linear 21,我打印出来看看它的结构是如何存储变量的。
那么这些,尽管稍后我们会训练多个样本,但大家读到这里时,可以理解我们正在为一个样本训练,以便更容易想象。那么因为我们有2个x,所以这里我们有两个W。W1和W2。正在打印权重。那么W1和W2。就是这样。然后打印偏置。偏置我们只有一个偏置。我们再加。而这里大家记住,这里它是一个矩阵。所以这里我们只有一个节点J,所以我们只有一个W。那么如果我们有很多J,那么以后它就是一个多行的矩阵。好的,目前线性层和逻辑回归问题它只有一个J,所以我们只有一行。这里我们有很多列是因为我们有两个特征X,所以我们有两列。就是这样。
好的,现在才赋值0.5。为什么是0.5?因为这里是W1。W2等于0.5和-0.1。所以已经赋值为0.5 5和负负0.1给这个,然后这里是B偏置0.1,这里偏置0.1,那么运行出来这里是0.5和负0.1,这里是0.1,这是用来做什么的?为了重复这张图,看看它是否能重现。
好的,好的,现在才计算什么呢?现在有这两个,有x和theta,那么当然我们不是取x theta或者x theta什么的,我们这里是线性层,我们会使用这个线性层,这个线性层我们就可以想象成,好的,输入是两个节点,输出是一个节点,那么它就是我们有这个输入,这个输出,然后连接起来的动作。那么,它就是这里的动作,例如J,这个是J,那么等于J。X1 X2,那么它等于X1 W1加X2 W2加B。它会自动在某个地方加上B。那么,所以我们只需要声明X。有两个X。一个是J。那么现在已经计算过了。
那么这里就当作我们有这个,对吗?复制到这里。就是这样。现在我重新计算x,x等于这个,那么我复制一下,这样快一点。下面已经创建好x了。X在某个地方,这里,对吗?已经创建好,我会慢慢地重新做,而不是直接给出代码,这样大家更容易跟上。那么创建的是1.5和0.2以及4.1和1.3 1.5。那些数字1就不用管了。数字1是它以后自己做的。现在我们这里就只放入x的值。这是两个特征。而数字1是这张图是向量化的添加。好的,1.5 0.2 4.1和1.3在这里。就是这样。然后我们有这个线性层,这里我们运行出来看看J等于多少。
好的,J等于0.8和2.0。就是这样。J = 0.8。8和2.0。上面有什么数字可以查吗?没有,对吗?这个是直接计算sigmoid了。好的。那么就直接计算sigmoid了。啊,我复制一下,这样快一点。现在我不打字了。复制快一点。在哪里?在上面,对吗?Sigmoid在这里,然后在这里创建sigmoid,然后运行它就会输出y帽,经过sigmoid就是y帽,对吗?然后我们传入j,然后是0.6 69和0.88。好的。0.69和0.88。那么就没问题了。假设这张幻灯片是正确的。就是这样。
那么此时我们看到,好的,0.69和0.88。好的,现在我们计算损失。损失我们用什么呢?这个二元的。二元,我声明。怎么声明呢?T.N. 二元有损失。损失函数。哦,这里它已经有现成的变量了。没关系,可以重复。torch.nn.binary_cross_entropy_loss。然后这个,如果我们之前没有安装numpy,大家就不会理解里面在做什么。它做起来看起来很快,但我们觉得它做得太好了,最终我们不理解它在做什么。这里它会输出L值。就是这样。这个在大家已经编写过numpy代码后才有意义。
然后我嗯嗯,两年前用Keras它更短,但太短不好。太短了,当你想修改什么时会非常困难。修改的需求是非常实际的。我去复制y。我复制快一点。我复制,我把Y直接放在这里,让它更近。y = 01,对吗?y = 01。第一个样本y = 0。第二个样本y = 1。所以是第一个样本。第一个样本y = 0。为什么我不放01呢?那么我给大家试试。首先我先不运行这个。那么我在这里打印形状。这里的形状是2x1,对吗?
那么2x1,y帽是2x1,那么这个y我们也必须做成2x1,以便它们匹配,这样就不需要处理,我们也能控制。所以这里这样赋值,它就是2x1。它就像上面的形状一样。这是2x1。然后计算损失,传入y帽和y,对吗?计算损失时,我们传入y帽和y。然后计算里面的交叉熵什么的,对吗?那么它输出0.65。好的。0.65。对吗?好的,好的,然后现在仍然做对了,因为刚才已经解释过这个了,那么就直接输入这个,尽管我们没有使用for zero c,对吗?
那么我声明,我再声明一遍,让大家看到,重复一遍,让大家更清楚地看到这个声明,复制整个这段。好的,声明这个。然后它会更新某些参数。更新这里的参数。线性层,对吗?传入并调用参数函数,参数是0.01。看看参数是多少,可能不是0.01。我们试试看它是否一样。
好的,然后我调用zero。在我们调用这个函数之前,我们调用zero。反向传播之后,打印出,呃,打印什么?啊,打印这两个。打印这个变量,对吗?就是这样。并打印偏置变量。就是这样。那么,啊,这里这里,对吗?这是两个变量W的导数值。W1 = 0.5,W2 = -0.1。然后不同了?啊,哪个错了?哪个错了,大家?我我我,大家。大家在这里调试,点我损失。那么大家有没有跟着我这里?此时有没有和我一起?正在做什么了?不不,还没还没step。现在再说一遍。目的正在做什么?调用loss.backward函数后,想打印出来看看权重的导数是多少,偏置的导数是多少,但我打印错了,大家帮我修改一下看看。我这样打印不行,那应该怎么打印呢?大家还记得吗?这个是我正在打印权重值,对了。点那个。所以很多时候我们编码时会有错误或bug。那么这里W1 = 0.2,W2 = 0.06 - 0.06。就是这样。然后这个等于0.28,对吗?-0.06,这个B是0.289,好的,对吗?那么就没问题了。
好的,然后我在这里给大家看全貌,如果它对了,我就直接删除这个,让它简洁一点。好的,就当作这个对了。那么现在我才更新。更新我们用什么?就用这个点step。点step啊。就是这样。然后我打印出两个变量,对吗?现在才打印出这两个变量,那么更新就是我们看看变量是如何变化的。就是这样。那么它会输出049减0.09 09,然后这个049这里选择的是W1,-0.09 W2,B=0.07,抱歉,0.097 0.097 1,对吗?也是1。好的。巧合了。通常后面的那些会四舍五入,这个没问题。就是这样。那么我们已经完成了一次逻辑回归问题的运行。就是这样。
逻辑回归问题的一次运行。好的,我才删除,给大家看。就是这样。当然,当我们实际运行时,我们不会在这里声明。当然不会。我们会在上面某个地方声明,对吗?好的,这个也一样,我们会在上面某个地方声明。然后我们不需要这样做。那么这里我们创建,这个是获取数据x,这个y必须放在这里。这个是我们从数据中加载出来的。那么当我们获取数据后,我们计算,我们计算
哎,你看。啊,这个也必须在这里声明。那些需要的东西都要声明。这个是模型,那么这个就是model。我们理解,我们理解它是一个model,它包含那个。Mode它再包含这个才对。看。这俩就是model。然后我们加载数据,计算输出,计算损失,更新。就那样。
然后,然后小河问为什么在zero grad那里要再次解释,为什么有这个step,而在上面做的时候却没有呢?然后,把这个删掉。第一个问题是,如果只运行一轮,那么删掉它能运行吗?答案是可以,但是如果我们运行两轮,也就是for循环的话,就必须有,那为什么呢?那是因为,因为我再说一遍。刚才已经说过这部分了,但我们说得更实际一点。等我一下,我翻一下代码。逻辑回归,大家等一下。哎,我们这里是专门做什么的?然后我们专门做minch。我们必须按照普通的方式编写代码。已经失败了。再试一次,如果不行,就翻回线性回归的课,等我一下。线性回归上面有很多样本了。这里设为0。看这里。每次这个就是一个step,大家从这里到这里。从这里,大家看,从for循环到这里我们更新。更新完成就是一个step。Step就是它执行这个动作,它会累加每个样本的导数,累加起来再除以平均值进行更新。然后它运行到下一个循环。那么在执行之前zero grad的动作,就是把这个全部设为0。如果不设为0,那么旧的导数仍然存在。就是这样。就那样。明白吗?就这么简单,没有什么特别的。好的,就这样。
然后因为之后有一些问题,我们不想,我们想继续做,它就不会为我们默认做。所以我们有没有zero grad?有的,大家。总是,总是有的。刚才在做的时候还没有介绍,因为想一步一步介绍,所以现在绕回来,总是很难,即使是新的反应问题,在计算导数之前,我们总是要清除所有内部变量,全部清除,全部更新,全部重新初始化为0,这样我们才能重新计算导数,很多时候这里面有很多样本,它是累积计算的,好的,这是逻辑回归问题。从总体上看,这是逻辑回归的代码,我们有很多,也是类似的数据。那么这时我们计算y帽,计算损失,然后计算导数并更新,就这样。那么它只有五行代码。而这是损失的声明,优化器的声明。剩下的是模型。好的。
呃,大家对这里有什么问题吗?因为有很多新的知识点,连续不断,对吧?大家有什么问题就直接讨论,以便复习。打开这个给你们看。在这个里面大家还有什么疑问吗?看看有没有什么问题?没有,对吧?已经看到都讨论完了。然后它才有一个这样的,没有问题,对吧?然后我们才有一个,呃,如下,例如y帽1是不是等于,例如我们有2个j1,等等,呃,然后试试y帽而不是y帽1。这不是softmax问题,而是,呃,逻辑回归问题,所以我们用sigmoid,那么它等于什么?1除以1加上e的负j次方,对吧?就是1除以e的负j次方。然后假设我们取对数。那么通常我们是不是计算y帽,y帽等于这个。然后我们计算什么函数,我们就会取y帽的对数,对吧?那么,y帽的对数,我们直接代入,等于1除以1加上e的负j次方的对数。那么它会等于log,log 1等于多少,大家?log 1等于0,对吗?log 1等于多少,对吧?帮我确认一下。log 1等于0。对的。这就是为什么这边是1除以p的对数,对吧?然后减去,减去1加上e的负j次方的对数。看。然后。那么这时我们看到1除以0最终等于这个。然后人们去进行数值分析。这个是人们去分析,然后他们发现,哦,当我们计算这个的时候。那么我们计算这个会比计算这个更稳定。看。为什么呢,可能我得再研究一下,因为它看起来有点长,所以我只取最终结果,就是计算这个。就是我们去计算e的幂,很多时候这个会溢出,然后什么的,所以人们研究发现这个会好一点,只是好一点。所以在计算方面,它的精度更好,也更稳定。人们有证明什么的,但我没有深入阅读。那么这时,与其先计算y帽,然后把这个y帽传给损失函数。损失函数会再计算一次这个。那么很多时候,很多时候它可能效率不高。在softmax问题中我们会看得更清楚。J1产生y帽1,J2到这里,那么y帽1是不是等于E的J1次方加上E的J1次方加上E的J2次方。看。然后,与其我们去计算这个,我们已经计算过一次e的幂了,对吧?然后我们又去计算一次log,这有点奇怪。既有e又去计算log,这有点奇怪。那么这时我们计算这个,它会等于e的j1次方的对数减去,呃,e的j1次方加上e的j2次方的对数,那么它等于j1然后减去这个,对吧?减去这个,其中,呃,这个e,E的J2次方会等于E的J1次方减J1加上J2次方,那么会等于,抱歉,这个,我减J1加上I1会等于J2减J1乘以E的J1次方,代入这里可以吗?不能在这里,那么它会等于log的,呃,这里有公因数,直接提取公因数E的J1次方乘以1加上E的J2次方减J1次方,对吧?然后等于J1减去E的J1次方的对数加上1加上E的J2次方减J1次方的对数。那么这个抵消这个,这个抵消了,对吧?那么最终等于负的1加上E的J2次方减J1次方的对数。那么人们研究发现,分析出来,与其我们用这个公式计算y帽,然后计算对数。那么这时我们只计算这个,直接计算这个。这个是我们直接传入J1和J2,对吧?那么我们直接计算log,它就会计算。也就是说我们不需要计算y帽,因为在计算和数值分析方面,人们分析认为这样更稳定。那是什么意思?那么是什么意思?那么重新运行这个意味着即使是Python的第一个版本,人们也没有意识到。那是因为创建这个版本,第一个Python版本的人,他们不是那边的专家,所以他们也不知道。因为那完全是另一个研究领域。所以很多时候我们,呃,一开始无法知道所有的事情。
Wave,对吧?那么回到这里,它出来是-0.16,对吧?是吗?更新之后,慢慢地,更新之后,这里我们没有更新后的值,很难计算,对吧?这个我们在这里计算。怎么出来不一样了?呃,线性,呃,出来不一样是因为我忘了想复制一份来做另一个,我再给你们描述另一个,抱歉大家。贴在这里。那么这个是0.49,49和-0.09,对吧?这是0.4,0.49和-0.09。看。那么这个完全没问题。但是后来Python他们才看了这边,看了那边,他们发现,啊,有这样一个技术,他们才说,好的。那么这样的技术,技术是怎样的?技术如下,就是别再计算这里了,别再计算y帽了。我们把这个y帽遮起来。因为根据刚才的,我们有负的log 1加上e的j2减j1次方。那么对于其他问题,而对于那个问题,对于逻辑回归,它是不同的公式。那么这时我们只需要传入J就行了,不要传入y帽进行计算,我们计算e的幂,然后过一会儿又取它的对数,很多时候这有点奇怪,因为log和e是相互抵消的,log和幂是相互抵消的,那么很明显,我们只要代入,很多时候我们就能尝试抵消掉一点点,那也会帮助我们减轻负担,所以首先我们这边减少,我们不再计算这个,也不再计算sigmoid,当然我们也不能用这个函数,对吧?那么那时我们会用另一个函数。那时Python会生成另一个新的函数,它是在后面版本出来的函数,所以名字有点奇怪。名字有点奇怪的地方是,因为它是在后面出来的,按理说它应该是默认值,但是默认已经用了这个,所以它反而有一个更难听的名字。with logit。Logit就是J。J就叫做logit了。有没有搞错名字?老师等一下。呃,我们运行这个,运行这个会产生这个,啊,天哪,这里应该代入J才对,对吧?代入y帽有什么用呢?J这个我们编程时经常会犯这个错误,复制粘贴很容易出错。那么还是旧的数字,对吧,大家?0.49和0.09。这就是为什么在Python中有两个函数。一个函数叫做binary cross entropy loss,这是我们理论上脑海中的标准函数。第二个函数是binary cross entropy with logits loss,这意味着输入是j而不是y帽。大家会在这里感到非常惊讶,如果大家听到会觉得,哦,怎么这么奇怪呢?那么那时大家又做了那个奇怪的事情,因为在早期使用keras,使用tensorflow,使用,呃,pytorch的时候,还没有那个版本。后来人们才领悟到,因为他们深入研究才领悟到。最初它总是先有基础的,所以这就是为什么这个才是标准。尽管名字不好听,但这个才是标准。它会帮助我们更好地进行计算。还有一个看起来很奇怪的是,人们只取J,而不是通过幂,而且它不是标准名称,对吧?
那么,那么它就是这个长名字,传入logit,logit就是字母J,所以我们有这个版本,大家。那么我上传到Drive的代码也有这个。有向大家介绍这个。好的,所以,所以总结一下,这个函数必须传入y帽。这是y帽。真正的y帽。通过mo转换y帽。而这个函数,这个函数我们只传入J的值,大家。只有J,然后我们记住这两个。好的,有点乱,大家记一下regr np loss是传入y帽。而这个函数传入J,是为了等一下softmax那里还有一个注释,以免混淆。好的,那么我们完成这个,我们去softmax。我们去softmax,做完就算今天结束了,对吧?
那么,就这样,同样地,呃,我们做softmax什么呢?Softmax我们不再有二元函数,我们有交叉熵函数。那么当人们领悟之后,交叉熵函数他们就不再添加“with logit”这个词了,而是取默认值,并且只有这一种类型。所以交叉熵总是接收J的值,大家。总是如此。注意,所以对于刚才的函数,刚才的是二元的。这个是交叉熵,它只有一种类型接收j,没有接收幂的类型了。那是因为它已经从那里学到了经验。好的,那么我们来看看softmax问题和,呃,逻辑回归问题有什么不同。那么等一下,我会找一下打印出网络结构的那个代码片段,以便大家更容易理解。这里有了,然后softmax就完了,那么在softmax中。现在,我向大家介绍一个叫做NN sequential的东西。刚才只是用了这个,对吧?看。只用了linear。现在我们才有一个叫做nn sequential的东西。它是什么?看。那么刚才如果我只有一个linear,那么我总是只有一个单一的层。现在假设我们有两三层,以后我们把它们堆叠起来,那么这时它就有一个数据结构。我们把这个理解成一个列表就行了。它只是那个列表。在这里面它为我们包含了一个linear。然后以后我们想再添加一个linear,我们就放在这里。再添加一个linear就放在这里。添加sigmoid就放在这里。看。那么sequential就只有这样的作用。然后这样括起来我们很容易看到。那么这个sequential我们理解它是一个模型。模型可以是逻辑回归模型,可以是softmax模型,或者其他什么模型,取决于我们在这里面放了什么。看。那么对于刚才的逻辑回归问题,如果我们使用binary cross entropy函数,它需要sigmoid,那么就会放入nn.sigmoid。那么这就是线性回归模型,抱歉,逻辑回归模型,它包含一个线性层和sigmoid。好的,那么我们有sequential,对吧?那么如果这里放2,这里放2。大家猜猜这里我们有多少个参数,这个sequential模型里面只包含一个linear,就好像它只有linear一样。与其把linear放在外面,不如把它放在模型内部的一个列表中,结果是一样的。看。那么有六个参数,对吧?2乘以3是6,对吧?那么有六个参数。好的,这六个参数都是可训练的参数。以后我们会学到这部分,它有一些不可训练的参数,稍后再说。看。那么这就是这个问题,这就是当我们为softmax问题使用,呃,一个线性层的时候。那么它的输出会是两个节点或更多,对吧?那么这个问题,它有二元分类的例子,那么我们就有两个J节点。分类,呃,三个类别,那么我们就有三个J节点。看。好的。那么这里假设我为哪个问题编写代码呢?我等一下。先说这个。现在我为,呃,这个问题编写代码,对吧?这个问题我们只有一个x,所以简单来说我们有一个linear 1。这是什么,大家?这个问题是,呃,二元分类。那么问号是多少?问号这里是多少呢?等于2,对吧?所以这里它会是linear 1 2,输入是一个x。那么然后我们进行二元分类,两个输出。两个j和两个y帽。那么现在我们试着计算。那么它也类似于我们讨论过的。它只不同在哪里?不同在于我们将使用交叉熵损失函数。而且在这个函数中,它已经从binary cross entropy with logits loss函数中吸取了经验。所以这里我们只传入j就行了。所以这里我们将传入J。所以只有这个是新的。所以与其像刚才讨论的那样重新输入,我已经准备好那段代码了,所以直接翻出来。我们慢慢地重新输入,就像刚才一样。那么直接翻出来。它在,呃,什么问题里?Softmax回归,对吧?好的,我慢慢和大家讨论。就是最初我们创建了一个linear 1 2,我们看看这个变量的结构是怎样的,1 2,那么它有两个V,两个W1 W2,并且它有bias 1和bias 2,然后我先看一下,我们有W命名为0和W1,我们有bias 0和bias 1,那么这时我们才能计算出两个相应的J,对吧?所以回到这里,我们有W0 W1和Bias 1,2,现在我手动设置4个参数,以便我们控制,所以我现在手动设置权重:W等于0.2和-0.1,0.2和-0.1 1,我们看看0.2减0.1,对吧?Bias是0.1和0.5,0.1和0.05,好的。B错了,对吧?B这样做是错的。我们必须注意。它的结构是一维的,那么我们必须做成一维的。如果我们做得不一样,它就会有不同的结果。看。然后我再运行一次,看看它是否正常,是否一样。看,它一样,结构相同吗?这是2 x 1。那么这里也是2 x 1。这是一个一维的,这里也是一维的。那么,就这样。0.2 - 0.1 1.0和0.05。那么,0.2和-0.1,0.1和0.05。好的,我们来计算一下J是多少。这里我们有两个样本。第一个样本是1.4,第二个样本是4.5。看。而y,当我们训练的时候,y我们不需要转换成one-hot编码,它会自动为我们做。第一个y是0,对吧?这里y=0,这里y=1。这是0的one-hot编码。这是1的one-hot编码。看。看。这是y=01,对吧?好的,我们来看看。那么这里先用一个样本计算。用一个样本1.4计算。好的,一个样本1.4,因为,呃,这里我们必须这样放。我们可以这样放,或者像这样放也可以。那么原因是什么?放在,放在课程结束时讨论图像时,大家会看得更清楚。当处理一个真实的,有60,000个样本的问题时,我们会看得更清楚。而现在我们先有一个,好的,一个样本,然后一个样本。一个样本我们必须放入,呃,放入一个张量。但是假设这个样本我们有很多特征怎么办?我们有两三个特征怎么办?所以它才会有这样的形状,因为以后我们有更多特征时,它就会是这样,对吧?所以即使我们有一个样本,它的形状也会是这样放置。一个样本,如果我们有更多,我们有超过一个样本,我们就换行继续做,就这样做。那么我们有两个样本4.5,就是这样。现在我先添加一个样本。一个样本运行出J。那么它会出来是0.38和-0.09,0.38和0.09,可以吗?好的,然后我们计算损失,计算损失,然后计算损失,那么这时我们传入J并直接计算损失,创建这个损失,我们直接传入J,但是我们不计算y帽,大家,损失这里等于y,y设为0,这个张量的0,抱歉,这个样本的0,那么它就会产生损失,然后我们正常更新,就像我们刚才学的那样更新。好的,然后我,然后我会把代码打包到一个里面,供大家观察密码。三个pass backward必须在这里,对吧?然后是step,然后是linear。好的,这是softmax回归。我们最初取出数据。然后我们计算j,只计算j。我们不要计算幂。看。然后我们计算损失,传入j和标签。然后我们计算导数并更新导数。好的,就这样。那么我们基本完成了,这样就完成了所有的问题。
那么回家后,大家如果想确保自己完全理解,就请重新阅读。大家重新阅读那个lesson demo文件。这个文件,大家阅读里面的文件就行了,但是lesson demo是我认为描述最清楚的,应该先读。那么大家读到哪里了?那么当大家能够自己重新做一遍时,当然现在大家什么都记不住。因为我们刚学,大家回去思考思考,然后记下那些函数和类,然后大家能够自己重新实现这三个问题,就算可以了。好的,那么这里我快速说一下sequential。刚才我们有那些层,那么现在sequential是一个概括。以后深度学习我们就会用到这个。sequential本身就是一个序列,一个列表。那么一个层的列表就变成了深度学习。那么今天我们已经,我们已经触及到深度学习了,大家。我们已经摸到深度学习了。下周我们就要进入深度学习了。那么这样的连接叫做全连接层,或者在,呃,Python中是线性层,对吧?那么这个,这个我们用的是linear,对吧?Linear 43,在里面,然后我们放入,我们放入softmax,那么这里只是设计方面,而我们的模型,这是理论上的图,大家。当我们向某人从理论上展示时,我们就给出这样的图。而当涉及到代码时,代码是为了实现,它不代表理论。所以当我们实现的时候,它只到J。那么,这个它自己,这个是它根据损失函数自动运行的。那么,但是当我们显示图片出来,让人们看我们的模型是怎样的时,大家仍然需要画出softmax。好的,例如这里是31,那么这里我们是31。这里是43,那么这里是43,对吧?那么这是一个库,只是用来打印这个。大家不需要记住这个。好的,那么对于softmax问题,那样就已经,就已经是一个softmax问题了,我们不要再把softmax函数放进去。因为交叉熵损失函数,呃,它已经包含了,呃,它已经包含了softmax函数了。好的,那么现在总结一下我们刚才讨论和看过的东西。
好的,这是一个特征x。那么我们线性回归问题的一个特征x。那么这个x,我们有一些参数,它会产生y帽。所以这里是1,对吧?好的,然后它会是11 1。我们这里面有两个参数。这两个参数。那么对于这个问题,我们有三个参数。三个x1 x2 x3。那么x1 x2 x3一个j。那么我们会有linear 31。那么,linear 31再加上一个bias,我们就有四个参数。在其他问题中,这个问题我们更多,那么这时我们数一下它有多少个参数。我们有13个特征。那么这里它会是linear 13 1,它再加上一个参数b就是14。看。那么我们就这样计算。好的,对于逻辑回归问题,参数数量不变。它只不同在于我们使用sigmoid函数。那么如果我们使用loss with logits函数,我们就不需要sigmoid,而如果我们使用binary cross entropy loss函数,那么那时我们就有sigmoid函数,大家把那些记下来,那么如果只是这样,那么我们使用这个损失函数,那么这已经是一个sigmoid模型了,不一定非要放入这个,抱歉,已经是逻辑回归了,不一定非要放入这个sigmoid,这个是11,所以这里是11 1,参数是两个参数,对吧?那么,这个问题有两列。那么这里会是21。因为sigmoid和逻辑回归它只返回一个节点。那么,所以这里是三个参数。这个1 2 3 4,对吧?那么它会是41。那么再加上bias就是5。好的,而对于softmax问题,softmax问题我们有这个损失函数,那么在这个损失函数中它已经集成了softmax函数在里面了,所以这里我们只返回J的值,所以这个的输出只返回J。看。所以这里我们有,我们有,呃,这个我们有一个特征,对吧?但是这是softmax的二元分类问题,所以我们必须有两个节点。看。那么一个节点连接着两个。一个节点连接着两个,也就是说,一个J节点连接着特征和bias。另一个J节点也连接着特征和bias。所以我们有2乘以2等于4,可以吗?我们有四个参数。好的,同样地,这里我们有三三
那个类。所以这里我们会有13。就是这样。那么我们有三个节点,所以3乘以2是6个参数,对吗?然后这个是1 2 3 4 1 2 3。那么我们有四个输入和三个输出。那么我们有3乘以5等于15,对吗?那么我们会有线性层,这里是1 2 3 4是3,所以是线性43,对吗?线性43这里加上3就是5,所以3乘以5等于15,15个参数。
好了,现在我们进入Q部分,然后我稍微讲一下图像。稍微讲一下让大家知道。好了好了,我们结束课程。好了,我们将在25点开始,各位。10:2 10:25 5 啊。
>> [音乐] >> 哦
>> [音乐] >> 呃
>> [音乐]
好了,德同学说要再展示一下softmax,对吗?稍后完成Q部分。那个上面也发了演示所有内容的那个代码。各位也可以从云盘上获取那个文件。好了,我们开始开始。好了。啊 [音乐] 有损失函数哦,各位。就当损失函数不存在。只是在谈论模型。好了,那么这个我们有两个特征,两个特征,我们有三个输出,所以它就是23,对吗?简单来说就是23。好了,然后下一题。
>> [音乐] >> 只有两个,而且两个都坏了。那么刚才说的,在softmax的那个练习里,对吗?那个交叉熵损失函数已经把softmax集成在它里面了。它里面已经自带softmax了。所以在模型设计方面,我们不要再附加softmax,就当我们传递J,以后我们只传递J。那么如果想传递J,它就只通过线性层,这里怎么能再通过softmax呢?也就是说,或者换句话说,在Smart的那个练习里,这个损失函数就在它里面。当这个损失函数运行时,它内部会调用它内部的softmax函数。所以在模型设计方面,我们只需要这样设计就可以了,而不是那样。这样的话,调用两次softmax也可以。这样这样才对。所以是A选项,各位。各位记住这一段。这个这个我们必须记住才能做。而当我们做softmax的练习时,因为softmax是以后深度学习的基础。以后我们会一直使用这个函数。所以当我们在设计模型时,我们只返回J值。在我们传入softmax之前的值,理论上我们会传入softmax,但从代码层面来看,就当去掉softmax。你记住这一段哦。
>> [音乐] [音乐] >> 好了,两个都对是不行的,因为这个地方这个地方,我们总是会有这个的,各位。那个第一个线性层,演示中没有这个,是慢慢地引入知识。意思是还没说,还没来得及说。所以我们总是会有这个,所以特别是我们有for循环,这肯定是毋庸置疑的。那个这个是它有重复。我们演示一次就关机,那没问题。但当我们多次运行训练时,肯定要有这一步。这一步是我们计算求和时将变量设为0的步骤。这个没有啊,所以不行。好了,只能有数字1哦。哦,-1啊,忘了说。各位猜猜看。各位猜猜看,刚才还没说,猜猜看。
>> [音乐] [音乐] >> 好了,等于那个。那么在这里,当我们的输入有一个特征,并且是softmax的练习时,这里是2 1 2,那么通常情况下,这里是输入,这里是输出,这个线性层的输出会是2,两个节点,因为这里是2,所以我们有四个参数,对吗?我们这里有偏置,2乘以2是4。但是当我们用PyTorch训练时,它支持我们用一个样本训练,用m个样本训练,或者用n个样本训练,想训练多少就训练多少。它只要放进去一个样本,就用一个样本训练;放进去10个样本,它就自动训练10个样本。代码没有任何改变。没有任何改变。但是在设计方面,我们正在设计,这里我们正在设计模型,那么在设计时,我们并不关心我们要训练多少个样本。当我们设计时,当我们设计完成后,用一个样本训练还是用n个样本训练都可以。想训练多少就训练多少。所以在Python中,当我们真正训练时,这时我们必须有一个参数来告诉Python我们正在训练多少个样本。所以在设计方面,它会显示数字1 -1。-1意味着它稍后会确定。它在我们实际运行时确定,它就是代表样本数量的数字。例如,我们训练32个样本,32张图片,或者是128张图片。那么这时,当我们实际运行时,我们传入128张图片,它会通过这个线性层。那么通过这个线性层,每张图片都会变成两个节点,变成两个数字。那么这里是128张图片,每张图片都变成两个数字。所以这个-1会在我们真正训练后确定。就是这样。那么这就是样本数量。当我们在NP中操作时,我们是不是有一个n乘以多少的矩阵。N乘以K,其中K是样本数量。N是,抱歉,K是特征数量,而n是样本数量,对吗?所以我们经常会有一个矩阵,这里的每一行都是这个矩阵,对吗?矩阵。矩阵,这里是行数。就是这样,这是行数,而对于每一行,它有两个列,对吗?就是这样,这是每一行有两个列。意思是每个样本都会在每一行上表示。每个样本表示为一行,行数就是样本数量。那么这就是样本数量,对吗?就是这样,和我们一模一样。就是这样,它在这里。好了,我们记住这个。它这里有一个样本数量的概念。选两个选项哦,各位。
>> [音乐] >> 好了。好了,我们来分析一下A B C D。啊,这是一个二元分类问题,这个二元分类就是逻辑回归,逻辑回归就是二元分类问题,对吗?这就是二元分类问题。如果我们采用逻辑回归的方式,那么这里我们有两列,这就是二元分类,那么我们只有一个输出,对吗?逻辑回归是二元分类。那么对于这个问题,从设计层面来说,这个从设计层面我们忽略损失函数的问题。那么对于二元分类问题,如果我们使用逻辑回归方向,并且使用sigmoid。那么输入是数字2。那个输入是数字2,这里都是2了。逻辑回归使用sigmoid的输出是1。那么这里是1。然后我们使用Smil。那么这就是完全可以应用于普通二元分类问题的方法。那个就是,当进行二元分类时,我们有两种方法:要么输出一个节点并使用sigmoid,要么输出两个节点并使用softmax。这是广义的二元分类问题,不一定是逻辑回归或softmax回归。以后我们把分类问题命名为二元分类问题,它前面有很多层,很多线性层堆叠在一起,但是如果我们输出一个节点,就使用sigmoid,这样就可以进行二元分类;如果输出两个节点,就使用softmax,那也只是二元分类而已。各位记住那个公式哦,公式是一个节点J就用sigmoid,两个节点就用softmax,也只是产生二元分类而已。所以这里是A和D,对吗?D是生成两个节点并使用softmax。而这个是生成两个节点使用sigmoid,那不行。两个节点用sigmoid怎么行。Sigmoid的输入只是一个数字而已。它又不是输入两个数字。那个,而这个softmax,softmax的输入必须是多个数字。因为那时它才能计算每种类型的概率。所以B和C不行。A D哦,各位。A和D。好了,也选两个答案。最后一题。
嘿。那么A选项和C选项,对吗?A选项,A选项是符合PyTorch标准的,我们使用交叉熵函数,抱歉,使用交叉熵函数就可以了。这里我们有4有3,那么它就是线性43。这样就可以了。我们把它放入交叉熵函数,可以顺利运行。那个,但是这里题目说我们可以自己重写损失函数。是的,各位。我们自己可以重写。以后会和大家讨论如何自己重写这些层,线性层可以自己重写。然后以后那些激活函数我们也可以自己重写。按照自己的想法重写这些类。那么假设这里我们自己重写损失函数,那么这时我们写一个没有softmax的损失函数。那么这时它就会是C选项。那么假设我们按照这里自己重写。就是这样。那么这里这里为什么错了?这里是什么?这里5就不对了。这里只有4个,那肯定是不对的。而这里也是5,那也不对了。这里只有4个。所以如果答案,如果问题要求选两个,那么这两个是最好的,对吗?好了,你on你dot啊 manipulated。好的。恭喜各位。
好了,看看我们接下来有什么,以及是否还有什么新知识。还有很多新的东西,但是我们学到哪里就讲到哪里,如果我全部讲完而你们还没用到,有时怕你们会忘记。所以我们理论讲到哪里,就讨论PyTorch到哪里。好了,这个只是复习,对吗?我们训练的话,在这个练习里是逻辑回归。逻辑回归的话,这个放在哪里都可以,只要它在这个位置前面就行。那个,有些代码它就直接放在这里,以免我们忘记。而s的话,也请add放在这里,各位放在哪里都可以。那个总的来说,在我们调用这个之前,我们一定要记住在一个循环中调用它。那么,是的,我们对所有样本进行训练,对吗?我们计算输出,计算损失,然后更新。那么这个地方,这是一个逻辑回归问题,所以这个模型只包含一个线性层。因为它本身已经接收了J值。这个这个只是J。这里的输出是J,而不是Y hat。好了,对于softmax问题也是一样。这里我们这个输出也只是J。那个,因为它本身内部已经调用了softmax函数。好了,这就是softmax问题的模型。我们创建了一个Sequential模型,以便我们习惯使用Sequential,但实际上我们只用一个这个也可以。但是这个是一个层,只是一个层,所以很多时候我们只是勉强使用,但实际上我们这样做才是最完整的。它是一种模型,内部包含一个层。好了,而这里,这个部分就像N一样,各位。回去各位分析输入,各位打印出形状,然后各位逐行分析形状哦。刚才我们是直接添加了现成的数据。而这里我们是从这些文件加载。各位读一下代码哦。Ad创建并发送给各位三个文件夹。线性回归、逻辑回归和softmax文件夹,各位只需阅读这三个文件夹中的内容。好了,那么这里我们训练时,这里我们有一个新东西叫做item。Item是很多时候我们有一个这样的张量数据类型。例如我们有6,那么这个地方是张量,那么张量很多时候想取数字6然后给matplotlib显示出来,但matplotlib怎么会理解张量呢?我们只想取数字6,我们点item,它就会返回那个数字6,所以我们经常会有这个。那么各位回去阅读这些文件中的行哦。各位读完后,如果可以的话,自己再写一遍代码。那么这些代码大约95%和ad给各位演示的代码一样。它们只差一个for循环和从CSV文件加载,这些我们已经在前几周做过了。好了,ad再讨论一下,为下一课打开大门。那个,也为了让我们尝试处理一种新的数据类型。啊,这个讨论只在5到10分钟内哦,各位。那个就是,对于图像数据,灰度图像数据,它会以一个网格的形式存储,就像一个矩阵。而且这里我们有P值。P的范围在0到255之间。而对于彩色图像,我们有三个这样的矩阵。那么它代表了红、绿、蓝三种颜色通道,这是通常的方式,但实际上有很多种颜色通道。那个,通常人们会存储红绿蓝,那是对人眼来说最适合的颜色通道。而对于数据处理什么的,有时会有一些颜色通道更好。那个,嗯,有些程序会按照高度、宽度和通道的顺序存储。而另一些程序则按照通道在前、高度、宽度的顺序存储。那么这个就是内存的组织方式。那么现在我们还不需要关心这部分,这是内存的组织方式,就像“好的,图像会存储在一个连续的数组中”,那么它要么存储第一个的红绿蓝,要么存储第二个样本的第二个像素的红绿蓝,以此类推,或者它这样做:它存储第一个像素的红色值,第二个像素的红色值,第三个像素的红色值,然后是第一个的绿色值,第二个的绿色值,第三个的绿色值,以此类推,它有很多种这样的方式。那么我们大致了解一下就可以了。而这些东西很久很久以前,当我们还在使用OpenCV和C语言编程时,操作它会依赖于速度。那么现在重要的是算法。现在这些平台做得非常好了。那么在Python中,那些张量或者那些图像会这样存储。那么这就是图像,这就是MNIST数据。我们有60,000个样本是图像,高度和宽度是28 x 28。就是这样。那么对于softmax问题,各位,这是28 x 28的张量数据,而在softmax中,我们将使用一个线性层,传入第一个参数和第二个参数。那个,传入第一个参数和传入第二个参数。那么这个数字不能是28 x 28,它必须是相乘后的数字,也就是784。所以在我们把这张图片输入softmax之前,我们必须有一个步骤,就是把它拉伸成一个向量。那就是flatten步骤。我们有另一个数据。那么在这里,ad按照类的方式,我们放在这里也可以。我们这里就像我们通常这样声明。那个,现在给类。这里我们有一个名为flat的函数。那么最初我们先flatten,以便将28 x 28拉伸。变成一个包含784个数字的向量,然后我们通过线性层。就是这样。那么这里我们这里通常是28,对吗?那么我们不能直接传入那些J节点。这里我们有10个类别。我们有10个类别,所以我们会有输出,我们有10个节点。所以我们不能传入二维数据。我们必须先flatten,让它变成784个节点,再加上这里的一个节点。这样我们才能做到。那么各位可以再阅读一下这段代码哦。它只差flatten这个地方。各位请再阅读一下。就是这样。好了,那么这个是应用于图像数据。好了,各位啊,哦,这只是阅读部分。也就是说,各位在后面的几个模块我们会更详细地讨论。而今天的课程主要是我们将应用于线性、逻辑回归和softmax问题,所以各位回去请重新做一下演示部分,它有lesson demo Jupiter文件,各位请用ad重新做一遍,然后各位再写一遍代码哦。那么这就是今天课程的结束。各位有什么问题吗?好了,我们讨论2到3分钟哦,各位。因为明天早上在胡志明市有个会议,所以现在就叫车去胡志明市了。啊,各位讨论了吗?好了,这节课后各位做一下练习,然后看视频哦。各位请看视频解答练习,用app。好了好了,各位回去编写今天的课程代码,因为知识点有点多,所以各位要多练习,熟练一下哦。好的,就这样。各位再见。下周再见。好了,啊,各位再见。好了,PyTorch它会在GPU上运行。