深度学习修炼(三)——自动求导机制

文章目录

    • 致谢
  • 3 自动求导机制
    • 3.1 传播机制与计算图
      • 3.1.1 前向传播
      • 3.1.2 反向传播
    • 3.2 自动求导
    • 3.3 再来做一次
    • 3.4 线性回归
      • 3.4.1 回归
      • 3.4.2 线性回归的基本元素
      • 3.4.3 线性模型
      • 3.4.4 线性回归的实现
        • 3.4.4.1 获取数据集
        • 3.4.4.2 模型搭建
        • 3.4.4.3 损失函数
        • 3.4.4.4 训练模型
    • 3.5 后记

致谢

Pytorch中常用的四种优化器SGD、Momentum、RMSProp、Adam - 简书 (jianshu.com)

反向传播算法(过程及公式推导)_好的嗡嗡嗡的博客-CSDN博客

动手学深度学习——矩阵求导之自动求导_时生丶的博客-CSDN博客

(1条消息) Pytorch入门(四)——计算图与自动求导_xinye0090的博客-CSDN博客

(1条消息) pytorch 计算图以及backward_coderwangson的博客-CSDN博客_pytorch清空计算图

3 自动求导机制

要明白自动求导机制,我们就要先知道计算图是什么。在后面的学习中,我们会学习到多层感知机,而为了计算多层感知机,我们求需要动用计算图和利用自动求导。

3.1 传播机制与计算图

pytorch框架和TensorFlow框架在计算图中最大的不同是:pytorch是动态的计算图,它能边搭建边运行;而TensorFlow是静态的计算图,它必须先把图搭建好后才能开始计算。

3.1.1 前向传播

我们来看看什么是前向传播。前向传播英文是(forward propagation或forward pass),它指的是:按顺序(从输入层到输出层)计算和存储神经网络中每层的结果。

如果你听不懂的话,可以看一下下面前向传播的计算图。

image-20220102182948332

上述的计算图反映了一件事,如果把上述过程看做是神经网络,那么前向传播实际上是在计算每一层的值。

3.1.2 反向传播

比较重要的是反向传播。反向传播(backward propagation或backpropagation)指的是计算神经网络参数梯度的方法。简而言之就是,该方法根据微积分中的链式规则,按相反的顺序从输出层到输入层遍历网络。同理,反向传播计算图如下图所示。

image-20220102183052989

反向传播实际上就是根据求导,来算出某一节点对于另外的某一节点所给的“回馈”。即导数,但是对于多个变量求偏导,我们叫其结果为梯度

3.2 自动求导

pytorch为我们提供了自动求导机制,其机制用torch.autograd来实现。为了照顾新手,我们不直接给出API,而采取循循引诱的方式来讲解。

对于pytorch的自动求导来说,由于版本的更新迭代,老版本的实现和新版本的实现大有差异,在老版本中,自动求导必须调用autograd.Variable来把变量包装起来,而新版本则不需要了,只需在需要自动求导的张量里添加requires_grad = True即可。

# 方法1
x = torch.randn(3,4,requires_grad = True)
x

out:

tensor([[-0.1225, 0.5622, 0.3288, -1.2560],
[-0.7067, 0.2453, 1.8471, 0.9765],
[-0.7606, 0.8300, -0.9079, -0.2566]], requires_grad=True)

上述的方法是下面方法的简化版。

# 方法2
x = torch.randn(3,4)
x.requires_grad = True
x

out:

tensor([[-0.3050, 1.6089, 0.4765, 0.8169],
[-1.4941, -0.9640, 0.4670, -1.5811],
[ 0.1837, -0.5159, 0.4066, 1.8279]], requires_grad=True)

以上的两个方法均可以使用。

我们单单只是构建张量可不够,只要一个张量要求啥子导。我们在构建一个张量b。

b = torch.randn(3,4,requires_grad = True)

做完上述的步骤后,我们构建一个没有权重的线性模型。

# 构建一个线性模型
t = x + b

当我们做完上述的步骤后,实际上一个比较简单的神经网络就搭建起来了。

如果我们把线性回归描述成神经网络。那么我们的输入层就是所有的特征,而输出层就是预测值。

在如图所示的神经网络中,输入为x1,x2...xdx_1,x_2...x_dx1,x2...xd,因此输入层中的输入数(由于我们常常把输入的特征放入向量,实际上向量的长度就是维度,故我们把输入数也叫特征维度)为d。

由于我们通常计算时发生在输出层里,输入层只是负责传入数据,所以一般输入层不算入层数,这么说下来,我们可以得出结论:这是一个单层神经网络。

再啰嗦几句,上面的输入层把数据输到输出层,所以给人感觉就好像输出层一下子要处理很多的输入(笑。。。不知道你能不能get到那种感觉),所以这大概率为什么这种输入到输出的变化被叫做全连接层(fully_connected layer)或称为稠密层(dense layer)的原因了。

好,回到我们的主题,既然已经搭好了一个线性回归模型了,我们怎么使其反向传播?我们知道,反向传播的前提条件是要知道前向传播中的输出值。所以我们继续往下:

y = t.sum
y

out:

tensor(-1.6405, grad_fn=)

在pytorch中,如果需要反向传播,只需在每个张量里指定自动求导,而后在最后一步调用以上方法即可。如在本例中,调用y.backward即可进行反向传播。

y.backward() 

而后,如果我们想看b的梯度,我们可以通过以下的方式:

b.grad

out:

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

这里可能会有一个疑问哈,y已经进行反向传播了,此时如果通过变量.grad调用的是什么梯度?因为你使用的是y的反向传播,拿b来举例,b.grad实际上就是求y对于b的偏导。

还有一个问题是,在上述的操作过程中,我们并没有打开t张量的自动求导开关,那他是否也能自动求导得到梯度呢?

我们调用tensor.requires_grad即可查看该张量是否打开了自动求导开关。

x.requires_grad,b.requires_grad,t.requires_grad

out:

(True, True, True)

以上的结果侧面印证了一个问题,即使没有指定某一张量可自动求导,但是和打开自动求导的张量进行运算时,其运算结果的自动求导开关也会被打开。

3.3 再来做一次

我相信经过上面的一次操作,你已经大概知道torch的自动求导机制了。我们趁热打铁,试着做一下下面的工作。

image-20220323105345688

如果我们要对上述的计算图计算的话,我们可以进行以下步骤:

# 计算流程
x = torch.rand(1)
b = torch.rand(1,requires_grad = True)
w = torch.rand(1,requires_grad = True)
y = w * x
z = y + b# 我们查看各参数的自动求导打开情况
x.requires_grad, b.requires_grad, w.requires_grad, y.requires_grad

out:

(False, True, True, True)

实际上,我们还可以把计算图看成计算树来查看哪些参数是叶子结点。

# 我们还可以看一下哪些参数为计算图(树)的叶子结点
x.is_leaf, b.is_leaf, w.is_leaf, y.is_leaf, z.is_leaf

out:

(True, True, True, False, False)

当上述处理完毕后,我们对z进行反向传播计算。

z.backward(retain_graph = True) # 保存计算图

然后我们分别查看下面参数的梯度。

w.grad

out:

tensor([0.3843])

b.grad

out:

tensor([1.])

你可以在jupyter notebook上执行完上述的两个参数的梯度后再执行一次,第二次时,你发现它们居然是第一次运行结果的两倍了。也是因为我们在z的反向传播时指定保存计算计算图,使得后面在计算某参数的梯度时,他都是根据上一次计算图的结果继续累加。

3.4 线性回归

继机器学习一别,我们已经许久未见狭义线性模型了。对此,我们希望在深度学习中能再次实现它。

3.4.1 回归

回归,英文名regression。在先修课机器学习中,我们经常能够遇见两个名词:回归分类。这两者的区别实际上就是:回归是根据以往的经验来预测未来的趋势或走向,比如说经典的房价预测问题;而分类是根据根据以往的经验来预测下一个东西是属于什么,经典的就是二分类问题;从宏观上来看,回归是相对于连续的,而分类是相对于离散的。

3.4.2 线性回归的基本元素

线性回归基于几个简单的假设:首先,假设自变量x和因变量y之间的关系是线性的,即y可以表示为x中元素的加权和,这里通常允许包含观测值的一些噪声;其次,我们假设任何噪声都比较正常,如噪声遵循正态分布。

为了解释线性回归,我们举一个实际的例子,我们希望根据房屋的面积和房龄来估算房屋的价格。接下来这里涉及到许多机器学习的术语;为了开发一个能够预测房价的模型,我们需要收集一个真实的数据集,这个数据集包括了以往房屋的预售价格、面积和房龄,当然,如果你要收集更多的特征也不是不行,但是我们目前从最简单的开始讲起。

在机器学习的术语中,我们把这部分收集来用作训练房价模型的数据集叫做训练数据集(training data set)训练集(training set)。每行数据或者用数据库的术语来说叫元组在这里称为样本,也可以称为数据样本(training instance)或者数据点(data point)。我们把试图预测的目标(在这个例子中指的是房屋价格)称为标签(label)或者目标(target)。预测所依据的自变量(在这个例子中指的是面积和房龄)称为特征(feature)或者协变量(covariate)

通常,我们使用n来表示数据集中的样本数。对索引为i的样本,其输入表示为:x(i)=[x1(i),x2(i)]Tx^{(i)} = [x_1^{(i)},x_2^{(i)}]^Tx(i)=[x1(i),x2(i)]T,其对应的标签是y(i)y^{(i)}y(i)

3.4.3 线性模型

线性假设是指目标可以表示为特征的加权和,也就是我们高中所熟悉的一次函数y = kx+b,只是在深度学习中,我们换成了y=w1x1+w2x2+....+by = w_1x_1 + w_2x_2 +....+by=w1x1+w2x2+....+b。其中www叫做权重(weight),b叫做截距(intercept),b在高中数学中叫截距比较多一点,但是在深度学习中它通常被叫做偏置(bias)。偏置是指当前所有特征都取值为0时,预测值应该为多少。虽然特征取值为0可能在我们说的预测房价的例子中并不存在,但是我们仍然需要偏置,因为如果没有偏置那我们的模型会受到限制。

严格来说,如果应用到房价预测的例子上的话,我们可以写出这样的式子:price=warea⋅area+wage⋅age+bprice = w_{area}·area+w_{age}·age+bprice=wareaarea+wageage+b

如果是单纯地一个特征就写一个x,那个式子就会变为:y=w1x1+w2x2+....wnxn+by = w_1x_1+w_2x_2+....w_nx_n+by=w1x1+w2x2+....wnxn+b,这样的话实际上不利于我们计算,而且不简洁。根据我们线性代数学过的知识,我们知道可以用向量存放特征,即x = {x1,x2,x3…xn},当然,这仅仅是一个样本,如果是多样本的话,我们可以用矩阵来放。X的每一行是一个样本,每一列是一种特征
KaTeX parse error: Undefined control sequence: \ at position 115: … \end{bmatrix} \̲ ̲
如同我们前面所说,下标表示第几个特征,上标表示第几个样本。

同样地我们也把权重w放进矩阵,那么模型简化为:y^=wTx+b\hat{y} = w^Tx + by^=wTx+b

其中w之所以加转置是因为矩阵乘法就是ATBA^TBATB。而w和x两个矩阵相乘后,由于b是标量,这个时候就会用到python的广播机制去相加。

在我们给定训练数据X和对应的已知标签y后,线性回归的目标就是找到一组权重向量w和偏置b,找到后这个模型就确定下来了;当有新的x进来后,这个模型预测的y能够和真实的y尽可能的接近。

虽然我们相信给定x预测y的最佳模型会是线性的,但我们很难找到一个有n个样本的真实数据集,然后算出来的结果真的是线性的,这根线一点弯曲都没有,这是不可能的。因此,即使我们确信他们的潜在关系是线性,我们也需要加入一个噪声项来考虑误差所带来的影响。

3.4.4 线性回归的实现

3.4.4.1 获取数据集

上面一直在吹理论,实际上,如果你看过我的机器学习中关于线性回归的阐述,应该是或多或少了解的。而本小节的重点,我们将会放在如何用torch提供的API去实现一个线性回归上。

继上一小节最后说的,我们很难找到一个数据集用于构建线性模型,为此,我们自己捏一个出来。

import numpy as np
import matplotlib.pyplot as pltx_values = [i for i in range(11)]
x_train = np.array(x_values,dtype=np.float32)
x_train = x_train.reshape(-1,1)
x_trainy_values = [2*i +1 for i in x_values]
y_train = np.array(y_values,dtype=np.float32)
y_train = y_train.reshape(-1,1)
y_train

捏造出数据集后,我们可视化这些数据集看一下长啥样。

plt.figure()
plt.scatter(x_train,y_train)
plt.show()

out:

image-20220323112223781

3.4.4.2 模型搭建

当我们获取到数据集后,我们要做的第二步是搭建一个线性模型。

#导入模块
import torch.nn as nn
import torchclass LinearRegressionModel(nn.Module):def __init__(self,input_dim,output_dim):super(LinearRegressionModel,self).__init__()self.linear = nn.Linear(input_dim,output_dim) #全连接层# 前向传播    def forward(self,x):out = self.linear(x)return out

在上述的类中,我们需要去继承torch.nn.Module包,以方便我们后续神经网络层搭建。继承完毕后,我们设定类中初始化方法,我们调用父类方法初始化,并且指定我们要搭建的线性回归神经网络的层,在这里,我们需要的仅仅是一个有输入和输出的全连接层,且其中不需要添加激活函数,我们调用nn.Linear(input_dim,output_dim)即可搭建,里面传入的参数分别是输入数据的维度和输出数据的维度,在线性回归中,通过输入数据和输出数据的维度全设为1即可。

除了初始化,我们还需要在类中添加一个前向传播成员函数,前向传播的计算实际上就是上面nn.Linear()输入数据后得出的结果。

搭建好神经网络后,我们需要实例化神经网络类并且为其传参。

# 实例化类
model = LinearRegressionModel(input_dim=1,output_dim=1)
model

out:

LinearRegressionModel(
(linear): Linear(in_features=1, out_features=1, bias=True)
)

3.4.4.3 损失函数

搭建好模型后,我们还需要搭建损失函数,以此训练模型。

epochs = 1000 #训练次数
learning_rate = 0.01 #学习率
# 优化器
optimizer = torch.optim.SGD(model.parameters(),lr = learning_rate)
# 优化器使用的误差函数
criterion = nn.MSELoss()

在上面的代码中,我们调用torch.optim包下的SGD(随机梯度下降法)来优化我们的训练模型,并且我们采用均方误差(Mean Square Error,MSE)进行梯度下降。SGD的有参构造器中需要传入我们神经网络对象中所有参数,并且还要传入一个学习率。

定义好损失函数后,下一步,我们就该进行训练了。

3.4.4.4 训练模型

我们着重训练我们的模型。

for epoch in range(epochs):# 每梯度下降一次,epoch+1epoch += 1# 前面的数据是array格式,torch只能处理tensor格式inputs = torch.from_numpy(x_train)labels = torch.from_numpy(y_train)# 梯度要清零每一次迭代optimizer.zero_grad()# 前向传播outputs = model(inputs)# 计算损失loss = criterion(outputs,labels)# 反向传播loss.backward()# 更新权重参数optimizer.step()# 打印每五十次梯度下降后对应损失值if epoch % 50 == 0:print(f'epoch {epoch}, loss {loss.item()}')

out:

epoch 50, loss 0.23870129883289337
epoch 100, loss 0.13614629209041595
epoch 150, loss 0.07765268534421921
epoch 200, loss 0.04429023712873459
epoch 250, loss 0.02526141330599785
epoch 300, loss 0.014408227056264877
epoch 350, loss 0.008217886090278625
epoch 400, loss 0.00468717934563756
epoch 450, loss 0.0026733996346592903
epoch 500, loss 0.0015248022973537445
epoch 550, loss 0.0008696810691617429
epoch 600, loss 0.0004960428341291845
epoch 650, loss 0.00028292808565311134
epoch 700, loss 0.0001613689964869991
epoch 750, loss 9.204218804370612e-05
epoch 800, loss 5.2495626732707024e-05
epoch 850, loss 2.9940983949927613e-05
epoch 900, loss 1.7078866221709177e-05
epoch 950, loss 9.74200611381093e-06
epoch 1000, loss 5.556627456826391e-06

在第一讲中我们不是说了tensor很重要吗,现在可以告诉你为什么重要了,因为神经网络只接收tensor格式。所以对于array的数据,我们必须将其先转换为tensor再进行处理。

我们还要先注意在前面我们说过计算图可以保存梯度,为了防止误操作,我们在每次梯度下降时,我们都要清零迭代后的梯度数据。

接着我们需要计算前向传播,而后根据前向传播的输出值,通过损失函数反向传播,反向传播后优化器内参数不会自动更新需要手动更新。

训练完模型后,我们可以看看模型的预测结果,这里你照做就行,后面我再和你解释这是怎么一回事。

predicted = model(torch.from_numpy(x_train).requires_grad_()).data.numpy()
predicted

训练好的模型如果效果十分好,那我们可以选择保存该模型,以便下次的使用。保存模型的方法如下:

torch.save(model.state_dict(),'model.pkl')

需要知道的是,模型的保存是以字典形式进行保存,保存的仅仅是模型的参数,并且保存的名字为’model.pkl’且。

如果我们需要读入模型,可以如下:

# 读取模型
model.load_state_dict(torch.load('model.pkl'))

最后,在整个训练过程中,实际上我们并没有用到GPU来加速我们的训练过程,这是因为我们的模型十分简单,所有的数据集也很少。在面对大数据的时候,我们通常需要GPU来加速我们模型的训练,其方法是在实例化神经网络类后,将该对象传出cuda设备。并且在后面将训练集传入模型的时候,也要传入cuda。

#将神经网络对象传给设备
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model.to(device)#将数据传入cuda
inputs = torch.from_numpy(x_train).to(device)
Labels = torch.from_numpy(y_train).to(device)

3.5 后记

这一讲中讲的自动求导机制并没有什么复杂,并不需要魔化它,你需要知道的仅仅是:在构建数据集张量的时候打开自动求导,并且先计算前向传播后根据前向传播的值,输入损失函数然后反向传播更新参数即可。其他的底层的东西如果你真想弄明白,何必呢,你搞底层就不会来看我这一篇笔记了不是。而且人家框架就是为了让你避免重复造轮子。

至于3.4中线性回归模型的搭建,这只是一个简单到不能再简单的torch的初体验,实际上,里面的很多方法包含有很多的参数,都是我们不宜直接一波灌输进去的,在后面的学习中,我们会逐步地去掌握它们。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/398605.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度学习修炼(四)——补充知识

文章目录致谢4 补充知识4.1 微积分4.1.1 导数和微分4.1.2 偏导数4.1.3 梯度4.1.4 链式求导4.2 Hub模块致谢 导数与微分到底有什么区别? - 知乎 (zhihu.com) 4 补充知识 在这一小节的学习中,我们会对上一小节的知识点做一个补充,并且拓展一个…

java使用POI jar包读写xls文件

主要使用poi jar来操作excel文件。代码中用到的数据库表信息见ORACLE之表。使用public ArrayList<Person> getPersonAllRecords()获得所有的记录。 1 public class PersonXLS {2 3 public static void main(String[] args) throws IOException {4 5 …

深度学习修炼(五)——基于pytorch神经网络模型进行气温预测

文章目录5 基于pytorch神经网络模型进行气温预测5.1 实现前的知识补充5.1.1 神经网络的表示5.1.2 隐藏层5.1.3 线性模型出错5.1.4 在网络中加入隐藏层5.1.5 激活函数5.1.6 小批量随机梯度下降5.2 实现的过程5.2.1 预处理5.2.2 搭建网络模型5.3 简化实现5.4 评估模型5 基于pytor…

Android 应用程序集成FaceBook 登录及二次封装

1、首先在Facebook 开发者平台注册一个账号 https://developers.facebook.com/ 开发者后台 https://developers.facebook.com/apps 2、创建账号并且获得 APP ID 图一 图二 图三 图四 图五 3、获取app签名的Key Hashes 值&#xff08;两种方式&#xff09; 3.1方法1&#xff1…

IKAnalyzer进行中文分词和去停用词

最近学习主题模型pLSA、LDA&#xff0c;就想拿来试试中文。首先就是找文本进行切词、去停用词等预处理&#xff0c;这里我找了开源工具IKAnalyzer2012&#xff0c;下载地址&#xff1a;(&#xff1a;(注意&#xff1a;这里尽量下载最新版本&#xff0c;我这里用的IKAnalyzer201…

C++从0到1的入门级教学(六)——函数

文章目录6 函数6.1 概述6.2 函数的定义6.3 函数的调用6.4 值传递6.5 函数的常见形式6.6 函数的声明6.7 函数的分文件编写6 函数 6.1 概述 作用&#xff1a;将一段经常使用的代码封装起来&#xff0c;减少重复代码。 一个较大的程序&#xff0c;一般分为若干个程序块&#xf…

PC实用手册

为什么80%的码农都做不了架构师&#xff1f;>>> ##Win10除了Edge/IE&#xff0c;其他浏览器打开和载入速度都很慢 解决办法&#xff1a;以管理员身份运行cmd&#xff0c;输入netsh winsock reset重置winsock&#xff0c;然后重启电脑即可 转载于:https://my.oschin…

MySQL之表的约束

一 介绍 约束条件与数据类型的宽度一样&#xff0c;都是可选参数 作用&#xff1a;用于保证数据的完整性和一致性主要分为&#xff1a; PRIMARY KEY (PK) 标识该字段为该表的主键&#xff0c;可以唯一的标识记录 FOREIGN KEY (FK) 标识该字段为该表的外键 NOT NULL 标…

eclipse静态部署tomcat

转载于:https://www.cnblogs.com/sprinng/p/4223798.html

jvm fastdebug

背景 RednaxelaFX 写道agapple 写道还有一个问题&#xff0c;就是在验证一些逃逸优化时&#xff0c;有些jvm参数用不了&#xff0c;比如-XX:printInlining&#xff0c;-XX:printAssembly&#xff0c;jdk用的是1.6.11和jdk1.6.18-XX:PrintInlining在product build的Sun JDK上可以…

hmailserver批量添加用户

2019独角兽企业重金招聘Python工程师标准>>> 将内容复制到txt文件中后缀改为vbs 将用户名密码替换为自己的 脚本内容如下: Option Explicit On Error resume nextDim obBaseApp Dim objFSO Dim objTextFile Dim strNewAlias,iDim scrreport Dim failed Dim added fa…

数据库杂谈(九)——事务管理

文章目录9 事务管理9.1 恢复机制9.2 事务和日志9.2.1 事务9.2.2 运行记录的结构9.2.2.1 活动事务表9.2.2.2 提交事务表9.2.2.3 日志9.2.3 提交规则和先记后写规则9.2.3.1 提交规则9.2.3.2 先记后写规则9.3 更新策略以及故障后的恢复9 事务管理 9.1 恢复机制 数据对一个单位是…

CSS邮件相关

转载于:https://blog.51cto.com/8465917/1758775

沙箱模式以及其使用到的IIFE

//沙箱//与外界隔绝的一个环境&#xff0c;外界无法修改该环境内任何信息&#xff0c;沙箱内的东西单独属于一个世界//360沙箱模式//将软件和操作系统进行隔离&#xff0c;以达到安全的目的//苹果手的app使用的就是沙箱模式去运行//隔离app的空间&#xff0c;每个app独立运行//…

深度学习修炼(六)——神经网络分类问题

文章目录6 分类任务6.1 前置知识6.1.1 分类6.1.2 分类的网络6.2 动手6.2.1 读取数据6.2.2 functional模块6.2.3 继续搭建分类神经网络6.2.4 继续简化6.2.5 训练模型6.3 暂退法6.3.1 重新看待过拟合问题6.3.2 在稳健性中加入扰动6.3.3 暂退法实际的实现6.4 后话6 分类任务 在这…

深度学习修炼(七)——卷积神经网络

文章目录7 卷积神经网络7.1 卷积网络和传统网络的区别7.2 卷积7.2.1 卷积过程画大饼7.2.2 图像的不变性7.2.3 互相关运算*(补充)7.2.4 图像颜色通道*(补充)7.2.5 步幅7.2.6 多次卷积7.2.7 边缘填充7.2.8 特征图的大小7.2.9 卷积参数共享7.3 池化7.4 整体网络架构7.5 后话7 卷积…

网络爬虫(一)——爬虫及其实现

文章目录1.1 爬虫概述1.1.3 网络爬虫和浏览器的区别1.1.2 网络爬虫的定义1.2 requests请求库1.2.1 requests基本概念1.2.2 疫情数据爬取1.2.3 get请求1.2.4 headers请求头1.2.5 Cookies验证1.3 Beautiful Soup解析库1.3.1 安装1.3.2 对象的创建1.3.3 find方法1.3.4 后话1.4 正则…

Windows五种IO模型性能分析和Linux五种IO模型性能分析

Windows五种IO模型性能分析和Linux五种IO模型性能分析 http://blog.csdn.net/jay900323/article/details/18141217 http://blog.csdn.net/jay900323/article/details/18140847 重叠I/O模型的另外几个优点在于&#xff0c;微软针对重叠I/O模型提供了一些特有的扩展函数。当使用重…

C++从0到1的入门级教学(十一)——友元

文章目录11 友元11.1 全局函数做友元11.2 友元类11.3 成员函数做友元11 友元 让我们引入一个例子来讲述友元是什么。 生活中你的家有客厅&#xff0c;有卧室&#xff0c;客厅所有来的客人都可以进去&#xff0c;但是你的卧室是私有的。对于认识的人来说你可以允许它进去&…

KeyMob:为国内应用开发者管理的广告聚合平台

为什么80%的码农都做不了架构师&#xff1f;>>> 应用开发者在应用中嵌入广告SDK的来源主要包括两种&#xff1a;使用移动广告平台与移动广告聚合平台。国内有多少家提供移动广告管理的平台&#xff1f;据统计&#xff0c;这两个版本&#xff0c;已经有四五十家。虽…