用梯度下降的方式来拟合曲线

文章目录

    • 1. 简述
    • 2. 理论原理
      • 以二次函数为例
      • 整体的梯度下降步骤:
    • 3. 编码实现
      • 初始化权重矩阵
      • 计算损失和梯度
      • 更新权重
    • 4. 结果
      • 首先对上一篇文章中的真实数据拟合。
      • 测试拟合高次曲线方程
        • 数据是2阶的,拟合方程是2阶的
        • 数据是4阶的,拟合方程也是4阶的。
        • 方程是4阶的,拟合方程是3阶的,(测试欠拟合)
        • 方程式4阶的,拟合方程是5阶的,(测试过拟合)
        • 数据是2阶的,方程是4阶的 (测试过拟合)
    • 5. 总结
    • 6. 还有可做的可视化方向
    • 完整源码

1. 简述

在之前的一篇文章opencv C++ 曲线拟合中为了拟合一条二次曲线,用超定方程的理论转为求最小二乘的方式来做,这是一个解析的方式求的解,也是全局最优的解。在深度学习中学到的函数是非常复杂的,不能保证是凸的,也没办法从解析的角度来计算一个最优解,这时最有效常用的方法就是梯度下降,为了加深对梯度下降的理解,所以这里以梯度下降来拟合多项式函数来探索一下。

2. 理论原理

以二次函数为例

设函数为 f ( x ) = a 0 + a 1 x + a 2 x 2 f(x)=a_0+a_1x+a_2x^2 f(x)=a0+a1x+a2x2,确定一组 a 0 , a 1 , a 2 a_0,a_1,a_2 a0,a1,a2的值,也就确定了二次函数,所以可以认为一组 a 0 , a 1 , a 2 a_0,a_1,a_2 a0,a1,a2值就是一个模型了。

样本为 ( x , y ) (x,y) (x,y) y ^ \hat{y} y^为模型的预测值,要评价一个模型好不好,我们可以用残差总和(loss)来表示,残差总和越小,模型越好。那我们的目标就是找一组 a 0 , a 1 , a 2 a_0,a_1,a_2 a0,a1,a2的值,使得下面的式子:
l o s s = Σ i = 1 N ∥ y i − y i ^ ∥ 2 2 loss = \Sigma_{i=1}^{N}\|y_i - \hat{y_i} \|_2^2 loss=Σi=1Nyiyi^22
最小即可。虽然在很多场合都会用MSE均方误差来做损失函数,但是为了方便手动计算梯度,就不用MSE了,两者回归出来的最优值是一样的。


W = [ a 0 a 1 a 2 ] W = \begin{bmatrix} a_0 \\ a_1 \\ a_2 \end{bmatrix} W= a0a1a2 , Z = [ 1 x x 2 ] Z = \begin{bmatrix} 1 \\ x \\ x^2 \end{bmatrix} Z= 1xx2 ,则 f ( x ) f(x) f(x)可以写为 f ( x ) = W T Z f(x)=W^TZ f(x)=WTZ

整体的梯度下降步骤:

  1. 初始化权重W
    直接随机初始化
  2. 计算拟合损失

    对每一组数据 z i z_i zi, y i y_i yi in (Z,Y), l o s s i = ( y i − y i ^ ) 2 = ( y i − W T z i ) 2 = ( y i − ( w 1 z i 1 + w 2 z i 2 + w 3 z i 3 ) ) 2 loss_i =(y_i-\hat {y_i})^2 = (y_i - W^T z_i)^2 = (y_i - (w_1z_{i1} + w_2z_{i2} + w_3z_{i3}))^2 lossi=(yiyi^)2=(yiWTzi)2=(yi(w1zi1+w2zi2+w3zi3))2

l o s s i loss_i lossi W W W的梯度就是 ∇ = [ − 2 ( y i − ( w 1 z i 1 + w 2 z i 2 + w 3 z i 3 ) z i 1 − 2 ( y i − ( w 1 z i 1 + w 2 z i 2 + w 3 z i 3 ) z i 2 − 2 ( y i − ( w 1 z i 1 + w 2 z i 2 + w 3 z i 3 ) z i 3 ] \nabla = \begin{bmatrix} -2(y_i - (w_1z_{i1} + w_2z_{i2} + w_3z_{i3})z_{i1} \\ -2(y_i - (w_1z_{i1} + w_2z_{i2} + w_3z_{i3})z_{i2} \\ -2(y_i - (w_1z_{i1} + w_2z_{i2} + w_3z_{i3})z_{i3} \end{bmatrix} = 2(yi(w1zi1+w2zi2+w3zi3)zi12(yi(w1zi1+w2zi2+w3zi3)zi22(yi(w1zi1+w2zi2+w3zi3)zi3
4. 更新权重W
W t + 1 = W t − η ∇ W_{t+1}=W_t -\eta \nabla Wt+1=Wtη η \eta η为学习率;
5. 输入下一个数据,重复步骤2,直到误差小于一定值或者迭代次数达到设定上限。

3. 编码实现

初始化权重矩阵

  
    def init_weights(self, order_of_polynomial):
        """init_weights 按照多项式阶数生成一个 阶数行1列的权重矩阵
        例如;order_of_polynomial为3,则生成的W为

        [[random1],
         [random2],
         [random3]]

        Args:
            order_of_polynomial (_type_): _description_

        Returns:
            weights: 阶数行1列的权重矩阵
        """
        W = np.random.randn(order_of_polynomial, 1)
        W = np.array(W,dtype=np.float)
        return W

计算损失和梯度

loss_i = (yi-y_pred)**2

gradient_i = (-2*(yi-y_pred)*zi).T # 和zi维度一样,(n,) 后一个维度为空

更新权重


    def update(self, W, gradient, learning_rate):
        """update 更新权重

        Args:
            W (np array): nx1的array
            gradient (np array): nx1的array
            learning_rate (float): 学习率

        Returns:
            weights: 更新后的权重
        """
        W = W - learning_rate*gradient
        return W

4. 结果

首先对上一篇文章中的真实数据拟合。

opencvC++曲线拟合
在epoches = 10000
learning_rate = 0.0001的条件下获得的结果。
在这里插入图片描述
权重矩阵:

W_best:[[ 0.97192164 -0.00196608 -0.00102088]]

在上一篇文章中用解析的方式球的的结果是
拟合方程:f(x) = [0.97301156] + ([-0.00231144]*x) + ([-0.00109041]*x^2)

从图像的拟合结果和对比最小二乘解的权重矩阵,可以发现最后的误差是很小的。
Wonderful!!!

测试拟合高次曲线方程

测试的样本我们可以先随机生成一个系数矩阵A,然后在x的某个区间内随机采样一些点X,用A和X来生成Y,这样的话我们用X,Y来拟合得到一个系数矩阵,和真实的A对比就知道误差怎么样了。

数据是2阶的,拟合方程是2阶的

8100/10000,loss: [3.51305315], min loss [3.51305315],learning rate : 1e-05
weights : [[-4.66950745  1.85807499]], W_best:[[-4.66950745  1.85807499]]
8200/10000,loss: [3.42047123], min loss [3.42047123],learning rate : 1e-05
weights : [[-4.67867211  1.8609997 ]], W_best:[[-4.67867211  1.8609997 ]]
8300/10000,loss: [3.33339115], min loss [3.33339115],learning rate : 1e-05
weights : [[-4.68756034  1.863836  ]], W_best:[[-4.68756034  1.863836  ]]
8400/10000,loss: [3.25148594], min loss [3.25148594],learning rate : 1e-05
weights : [[-4.69618048  1.86658659]], W_best:[[-4.69618048  1.86658659]]
8500/10000,loss: [3.17444808], min loss [3.17444808],learning rate : 1e-05
weights : [[-4.70454061  1.86925406]], W_best:[[-4.70454061  1.86925406]]
8600/10000,loss: [3.10198832], min loss [3.10198832],learning rate : 1e-05
weights : [[-4.71264857  1.87184095]], W_best:[[-4.71264857  1.87184095]]
8700/10000,loss: [3.0338346], min loss [3.0338346],learning rate : 1e-05
weights : [[-4.72051196  1.87434969]], W_best:[[-4.72051196  1.87434969]]
8800/10000,loss: [2.96973104], min loss [2.96973104],learning rate : 1e-05
weights : [[-4.72813815  1.87678267]], W_best:[[-4.72813815  1.87678267]]
8900/10000,loss: [2.90943694], min loss [2.90943694],learning rate : 1e-05
weights : [[-4.7355343   1.87914216]], W_best:[[-4.7355343   1.87914216]]
9000/10000,loss: [2.85272592], min loss [2.85272592],learning rate : 1e-05
weights : [[-4.74270734  1.88143041]], W_best:[[-4.74270734  1.88143041]]
9100/10000,loss: [2.79938505], min loss [2.79938505],learning rate : 1e-05
weights : [[-4.74966401  1.88364956]], W_best:[[-4.74966401  1.88364956]]
9200/10000,loss: [2.74921405], min loss [2.74921405],learning rate : 1e-05
weights : [[-4.75641082  1.88580172]], W_best:[[-4.75641082  1.88580172]]
9300/10000,loss: [2.70202456], min loss [2.70202456],learning rate : 1e-05
weights : [[-4.76295412  1.8878889 ]], W_best:[[-4.76295412  1.8878889 ]]
9400/10000,loss: [2.65763939], min loss [2.65763939],learning rate : 1e-05
weights : [[-4.76930002  1.88991308]], W_best:[[-4.76930002  1.88991308]]
9500/10000,loss: [2.61589189], min loss [2.61589189],learning rate : 1e-05
weights : [[-4.77545449  1.89187616]], W_best:[[-4.77545449  1.89187616]]
9600/10000,loss: [2.57662531], min loss [2.57662531],learning rate : 1e-05
weights : [[-4.78142331  1.89377998]], W_best:[[-4.78142331  1.89377998]]
9700/10000,loss: [2.53969223], min loss [2.53969223],learning rate : 1e-05
weights : [[-4.78721206  1.89562634]], W_best:[[-4.78721206  1.89562634]]
9800/10000,loss: [2.50495396], min loss [2.50495396],learning rate : 1e-05
weights : [[-4.79282619  1.89741698]], W_best:[[-4.79282619  1.89741698]]
9900/10000,loss: [2.47228008], min loss [2.47228008],learning rate : 1e-05
weights : [[-4.79827095  1.89915358]], W_best:[[-4.79827095  1.89915358]]
true weights:[-5  2],predicted weights:[[-4.80349946  1.90082118]]

在这里插入图片描述

数据是4阶的,拟合方程也是4阶的。

true weights:[-10  -9   6  -5],predicted weights:[[-9.94695159 -9.02171446  5.97634426 -5.00084477]]

在这里插入图片描述

方程是4阶的,拟合方程是3阶的,(测试欠拟合)

true weights:[-10  -6   9  10],predicted weights:[[-8.23658869 49.58531876 10.11229995]]

在这里插入图片描述

方程式4阶的,拟合方程是5阶的,(测试过拟合)

true weights:[ 1 -9 -2 10],predicted weights:[[ 0.96926572 -7.09732105 -1.80982351  9.71279898 -0.03855437]]

在这里插入图片描述

数据是2阶的,方程是4阶的 (测试过拟合)

true weights:[10 -5],predicted weights:[[ 8.15489344 -3.46121244  0.75647929 -0.39215996]]

在这里插入图片描述

5. 总结

对于欠拟合的系统,模型的容量无法完整的表示数据,对于过拟合的系统,模型容量比较富余,当增加增加样本后模型的泛化能力就可以提高。在上面的过拟合的例子中增加样本数就可以更好的拟合真实的多项式。
在阶次较高的数据中,当x比较大时,计算中容易产生nan,所以这里的梯度下降并不能完全解决多项式拟合的问题,主要是在数值计算上,初始化在某些地方模型不收敛。但是这个项目对于理解梯度下降是一个非常好的角度,手动计算梯度,手动更新权重…。

6. 还有可做的可视化方向

查看权重的走向:
以2阶的方程为例,设权重为w1,w2, 以w1和w2为平面坐标的两个轴,对平面上的每一点(每一个都表示一个权重矩阵)都计算一次模型对数据的拟合损失,这样可以得到一个loss(w1,w2)的函数,用mesh可视化这个二维函数就可以知道在不同地方的损失,标记best weights的移动位置就可以更形象的看到梯度下降导致的权重矩阵的走向了。

完整源码


import numpy as np
import matplotlib.pyplot as plt
import random


class MyModule():
    def __init__(self) -> None:
        pass
        
    def init_weights(self, order_of_polynomial):
        """init_weights 按照多项式阶数生成一个 阶数行1列的权重矩阵
        例如;order_of_polynomial为3,则生成的W为

        [[random1],
         [random2],
         [random3]]

        Args:
            order_of_polynomial (_type_): _description_

        Returns:
            weights: 阶数行1列的权重矩阵
        """
        W = np.random.randn(order_of_polynomial, 1)
        W = np.array(W,dtype=np.float)
        return W

    def update(self, W, gradient, learning_rate):
        """update 更新权重

        Args:
            W (np array): nx1的array
            gradient (np array): nx1的array
            learning_rate (float): 学习率

        Returns:
            weights: 更新后的权重
        """
        W = W - learning_rate*gradient
        return W

    def trans_to_struct_data(self, X, order_of_polynomial):
        """_trans_to_struct_data 把x转为范德蒙德矩阵形式的变量

        Args:
            X (np array): nx1
            order_of_polynomial (int): 多项式的阶数

        Returns:
            Z: np array,[[1,x,x^2,x^3,...,x^order_of_polynomial],...]
        """
        sample_num = X.shape[0] 
        Z = np.zeros((sample_num, order_of_polynomial),dtype=np.float32)
        mul_item = np.ones((sample_num,))
        for i in range(order_of_polynomial):
            Z[:,i] = mul_item
            mul_item *= X
        return Z

    def fit(self,X, Y, order_of_polynomial, epoches=30, learning_rate=0.001):
        """fit 根据指定的阶数拟合曲线

        Args:
            X (_type_): _description_
            Y (_type_): _description_
            order_of_polynomial (): 拟合多项式的阶数,至少是一阶的
        """
        if order_of_polynomial < 1:
            raise ValueError("拟合多项式的阶数,至少是一阶的")
              
        W = self.init_weights(order_of_polynomial)
        W_best = W
        min_loss = 99999999999999
        Z = self.trans_to_struct_data(X, order_of_polynomial)
        for epoch in range(epoches):
            gradient = np.zeros((order_of_polynomial, 1)) # nx1的矩阵
            loss = 0.0
            for zi,yi in zip(Z,Y):
                # yi 是标量,一个数
                # zi 是一个样本,长为n的向量,numpy中没有像矩阵一样有两个维度
                # W 权重,nx1矩阵
                y_pred = np.dot(W.T, zi) # 预测值
                loss_i = (yi-y_pred)**2
                # gradient_i = np.array([2*zi]).T # 一维向量-2*zi转为矩阵后的转置,nx1
                
                gradient_i = (-2*(yi-y_pred)*zi).T # 和zi维度一样,(n,) 后一个维度为空
                gradient_i = np.array([gradient_i]).T # 增加一个维度,和gradient一致
                W = self.update(W,gradient_i,learning_rate)
                loss += loss_i
                gradient += gradient_i
            # W = self.update(W,gradient,learning_rate)
            if loss < min_loss:
                min_loss = loss
                W_best = W
            if epoch % 100 ==0:
                print(f"{epoch}/{epoches},loss: {loss}, min loss {min_loss},learning rate : {learning_rate}")
                print(f"weights : {W.T}, W_best:{W_best.T}")
        return W_best


def dataset_pool_n(n, sample_num=100, max_abs_x=1, if_rand=False):
    """生成随机多项式分布的样本

    Args:
        n (int): 阶数
        sample_num (int, optional): 生成的样本数量. Defaults to 100.
        max_abs_x (int, optional): x的最大值. Defaults to 1.
        if_rand (bool, optional): 为True时,在标准的数据上加随机波动,. Defaults to False.

    Returns:
        _type_: _description_
    """
    # 系数矩阵,标准的多项式系数
    A = [] # a0+a1x+a2x+...
    for i in range(n):
        A.append(random.randint(-10,10)) # 系数的绝对值在10以内,太大了的话次数高的数值太大
    A = np.array(A)
    
    # 随机生成一些x
    X = []
    for i in range(sample_num):
        x = 1 - 2*random.random()
        x *= max_abs_x
        X.append(x)
    Z = np.zeros((sample_num, n))
    mul_item = np.ones((sample_num,))
    for i in range(n):
        Z[:,i] = mul_item
        mul_item *= X
    
    # 用A和Z计算Y
    Y = []
    for zi in Z:
        y = np.dot(A,zi) 
        if if_rand:
            y += + (0.5 - random.random())
        Y.append(y)
    return A,np.array(X),np.array(Y)



def dataset_pool_two():
    X = np.array([-10.,  -9.,  -8.,  -7.,  -6.,  -5.,  -4.,  -3.,  -2.,  -1.,   0.,
         1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.], np.float)
    Y = np.array([0.890928, 0.904723, 0.921421, 0.935007, 0.94281 , 0.949828,
        0.966265, 0.975411, 0.978693, 0.97662 , 0.974468, 0.967101,
        0.957691, 0.958369, 0.949841, 0.932791, 0.9213  , 0.901874,
        0.879374, 0.868257], np.float)
    return X,Y



def main():
    
    order_of_polynomial_for_data_generation = 4 # 生成数据时用的阶数
    sample_num = 100 # 样本数量
    max_abs_x = 3 # x的最大值,当方程次数较高时,x值太大容易导致计算中产生nan
    if_rand = True # 为True时,在标准的数据上加随机波动,

    # X, Y = dataset_pool_linear()
    # X, Y = dataset_pool_two()
    A, X, Y = dataset_pool_n(order_of_polynomial_for_data_generation, sample_num, max_abs_x, if_rand)
    
    epoches = 10000
    learning_rate = 0.00001
    
    model = MyModule()
    order_of_polynomial_for_fit = 4 # 拟合数据时用的阶数
    W = model.fit(X,Y,order_of_polynomial_for_fit,epoches,learning_rate)

    print(f"true weights:{A},predicted weights:{W.T}")

    # 可视化
    X_axis = np.arange(-max_abs_x, max_abs_x,2*max_abs_x/sample_num)
    Z = model.trans_to_struct_data(X_axis,order_of_polynomial_for_fit)
    Y_pred = [np.dot(W.T,zi) for zi in Z]
    Y_pred = np.array(Y_pred)
    plt.scatter(X,Y,label="true sample")
    plt.plot(X_axis,Y_pred,c='red',label="predicted curve")
    plt.xlabel("x")
    plt.ylabel("y")
    plt.title("fit curve")
    plt.legend()
    plt.grid(True)
    plt.show()
    
    

if __name__ == '__main__':

    main()

        

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

可配置物料-文章资料分享

可配置物料项目一般很少用到&#xff0c;用到就是要命&#xff0c;推荐一下之前查资料收集的资料&#xff0c;分享给大家。感谢这位大佬收集的文章&#xff01; Variant Configuration (LO-VC) - Product Lifecycle Management - Support Wikihttps://archive.sap.com/document…

研报精选230419

目录 【行业230419东吴证券】AACR2023本土药企临床进展&#xff1a;“秒懂”全球创新药系列研究之会议追踪 【行业230419浙商证券】大制造行业估值手册&#xff1a;周度数据跟踪 【行业230419东方财富证券】2023上海车展前瞻&#xff1a;自主争先&#xff0c;迎接智能电动新时代…

Linux网络服务之DHCP篇

目录 一、了解DHCP服务1.1DHCP定义1.2DHCP好处1.3DHCP的分配方式 二、DHCP工作过程三. 使用DHCP动态配置主机地址 一、了解DHCP服务 1.1DHCP定义 DHCP&#xff08;动态主机配置协议&#xff09;是一个局域网的网络协议。指的是由服务器控制一段IP地址范围&#xff0c;客户机登…

【Mysql】分库分表

【Mysql】分库分表 文章目录 【Mysql】分库分表1. 介绍2. 拆分策略2.1 垂直拆分2.1.1 垂直分库2.1.2 垂直分表 2.2 水平拆分2.2.1 水平分库2.2.2 水平分表 3. MyCat3.1 概述 1. 介绍 采用单数据库进行数据存储存在以下瓶颈&#xff1a; IO瓶颈&#xff1a;热点数据太多&#x…

java IO流_1

目录 分类 字节流 InputStream OutputStream 文件拷贝 字符流 FileReader FileWriter 处理流 BufferedReader BufferedWriter 文本拷贝 流是从起源到接受的有序数据&#xff0c;通过流的方式允许程序使用相同的方式来访问不同的输入/输出源。 分类 按数据…

4.26和4.27、selectAPI介绍(4.27、select代码)

4.26和4.27、selectAPI介绍&#xff08;4.27、select代码&#xff09; 1.selectAPI介绍①select多路复用流程图②select多路复用缺点 2.select代码使用介绍3.select代码实现①select服务端实现②select客户端实现 1.selectAPI介绍 主旨思想&#xff1a; 首先要构造一个关于文件…

echarts中横坐标显示为time,使用手册

需求&#xff1a; 后端传递&#xff08;两段数据&#xff0c;不同时间间隔&#xff09;的24h实时数据&#xff0c;前端需要根据24小时时间展示&#xff0c;要求&#xff1a;x轴为0-24h&#xff0c;每个两小时一个刻度 误区&#xff1a; 刚开始通过二维数据的形式秒点&#xff…

MySQL数据库从入门到精通学习第2天(创建数据库)

创建数据库 通过CREATE DATABASE语句来创建数据库通过CREATE SCHEMA语句来创建数据库通过IF NOT EXISTS进行判断创建 通过CREATE DATABASE语句来创建数据库 创建数据库的语法格式如下&#xff1a; CREATE DATABASE 【数据库名】; 创建数据库的库名跟标识符一样也是有要求的&…

设计模式-结构型模式之桥接模式

2. 桥接模式 2.1. 模式动机 设想如果要绘制矩形、圆形、椭圆、正方形&#xff0c;我们至少需要4个形状类&#xff0c;但是如果绘制的图形需要具有不同的颜色&#xff0c;如红色、绿色、蓝色等&#xff0c;此时至少有如下两种设计方案&#xff1a; 第一种设计方案是为每一种形状…

android sdl编译

SDL&#xff08;Simple DirectMedia Layer&#xff09;是一套开放源代码的跨平台多媒体开发库&#xff0c;使用C语言写成。SDL提供了数种控制图像、声音、输出入的函数&#xff0c;让开发者只要用相同或是相似的代码就可以开发出跨多个平台。 1 下载SDL源码 http://www.libsd…

这篇把「精准测试」算是讲明白了

作为测试同学&#xff0c;我们经常在工作中会有这样的困惑&#xff1a;我写的用例真的有效且全面吗&#xff0c;我的测试真的做到有效覆盖了吗&#xff1f;回归阶段我到底需要回归什么&#xff0c;回归验证充分吗&#xff1f;这次的改动到底影响范围有多大&#xff1f;针对以上…

从编译器角度理解C++编译和连接原理

C编译链接整体介绍 链接主要工作 1 所有.o文件段的合并&#xff0c;符号表合并后&#xff0c;进行符号解析 链接时就是在符号表中找对应的符号是否只出现于.text或.data段一次&#xff0c;若一次都无&#xff0c;则符号未定义&#xff1b;若出现多次&#xff0c;符号重定义 符…

学习风`宇博客用户权限菜单模块

文章目录 用户-角色-菜单-资源 各表关系图菜单 和 路由菜单表及分析分析 /api/admin/user/menus接口MenuServiceImpl#listUserMenus接口返回示例及分析 前端代码分析menu.jsSideBar.vue 接口权限控制资源表 及 分析分析 WebSecurityConfig权限控制整体流程先说登录UserDetailsS…

行业那么多,为什么计算机领域这么火?

行业那么多&#xff0c;为什么计算机领域这么火&#xff1f; 计算机领域火已经不是一天两天了&#xff0c;从开始的进入互联网时代、到“互联网”、再到大数据、人工智能时代、数字化经济……计算机技术从行业内部的自我发展逐渐渗透到各行各业&#xff0c;甚至成为社会整体经济…

JVM-GC回收机制

目录 1.判定垃圾 1.引用计数 2.可达性分析 2.清理垃圾 1.标记清除 2.复制算法 3.标记整理 4.分代回收 上文讲述的Java运行时内存划分,对于程序计数器,虚拟机栈,本地方法栈来说,生命周期是和线程有关的,随着线程而生,随线程而灭,当方法结束或者线程结束时,它们的内存就自…

【Python_Scrapy学习笔记(十)】基于Scrapy框架的下载器中间件创建代理IP池

基于Scrapy框架的下载器中间件创建代理IP池 前言 本文中介绍 如何基于 Scrapy 框架的下载器中间件创建代理IP池。 正文 1、添加中间件的流程 在 middlewares.py 中新建 代理IP 中间件类在 settings.py 中添加此下载器中间件&#xff0c;设置优先级并开启 2、基于Scrapy框…

软件安全之CRC检测

CRC介绍 在玩某些游戏&#xff0c;例如fps类游戏时&#xff0c;你想要修改某些特定的数值实现一些功能&#xff0c;这时你很有可能会被查封账号甚至禁封机器码。因为你更改了游戏中的数据&#xff0c;从而导致接收方收到”错误的数据“。为尽量提高接收方收到数据的正确率&…

最新版本VSCode配置Python、PyQt5、QtDesigner环境并创建一个ui界面测试

参考链接&#xff1a;最新版本VSCode配置Python、PyQt5、QtDesigner环境并创建一个ui界面测试 一、安装Python3 PyQt5所支持的python版本是从3.5开始的&#xff0c;因此安装的Python3版本必须大于3.5。 我安装的位置是C:\Python\Python38。 参见真小白入门Pyhton的安装 二、安…

项目协同中的git

在远程代码仓库&#xff08;云效&#xff0c;gitee&#xff0c;github&#xff0c;Coding等&#xff09;新建一个代码库&#xff0c; 我使用的云效 新建一个develop分支&#xff0c;后续所有人的提交代码都合并到develop分支上面&#xff0c;一般develop分支是用来开发用的&…

NVM-无缝切换Node版本

NVM-无缝切换Node版本 如果未使用nvm之前已经下载了node&#xff0c;并且配置了环境变量,那么此时删除这些配置(Node的环境以及Node软件),使用nvm是为了在某些项目中使用低版本的node NVM下载 进入github的nvm readme&#xff1a; https://github.com/coreybutler/nvm-windows…
最新文章