AutoCV第七课:ML基础

news2024/11/15 11:15:14

目录

  • ML基础
    • 前言
    • 1. 复习sqrt函数
    • 2. 线性回归预测房价
      • 2.1 问题分析
      • 2.2 代码实现
      • 2.3 总结
    • 个人总结

ML基础

前言

手写AI推出的全新保姆级从零手写自动驾驶CV课程,链接。记录下个人学习笔记,仅供自己参考。

本次课程主要学习复习 sqrt 函数和线性回归预测房价。

课程大纲可看下面的思维导图。

在这里插入图片描述

1. 复习sqrt函数

我们正式进入人工智能的学习,首先我们来回顾下之前求取 2 \sqrt 2 2 的例子

示例代码如下:

def sqrt(x):

    p  = x / 2  # 初始化
    
    lr = 0.01   # 学习率

    def forward(p, x):
        return (p * p - x) ** 2
    
    def backward(p, x, lr):
        delta_p = 4 * (p * p - x) * p
        return p - lr * delta_p

    while True:
        
        loss = forward(p, x)
        if loss < 1e-5:
            
            break
        
        p = backward(p, x, lr)

    return p

print(sqrt(2))

上述求解 2 \sqrt 2 2 的代码与我们之前讲解的有所不同

在这段代码中,函数 forward 计算了当前参数 p p p 的预测值与目标值 x x x 之间的误差,可以看作是神经网络中的损失函数

而函数 backward 则根据当前的误差以及学习率 l r lr lr,通过对参数 p p p 求梯度更新参数值,可以看作是神经网络中的后向传播过程

因此,可以把这段代码看作是一个简单的单层神经网络,使用梯度下降法来求解 2 \sqrt 2 2

2. 线性回归预测房价

我们从房价预测的案例入手来讲解线性回归

回归模型可以分为一元线性回归和多元线性回归

一元线性回归是指一个自变量与一个因变量之间的线性关系的回归模型。例如,本次讲解的例子中我们可以用一元线性回归模型来研究房价与时间之间的关系。一元线性回归模型的数学描述如下:
h θ ( x ) = θ 0 x 0 + θ 1 x 1 ( x 0 = 1 ) h_{\theta}(x)=\theta_0x_0+\theta_1x_1(x_0=1) hθ(x)=θ0x0+θ1x1(x0=1)
即利用一条直线来拟合房价与时间之间的关系:
y = k x + b y = kx+b y=kx+b
多元线性回归是指包含两个或两个以上自变量与一个因变量之间的线性关系的回归模型。例如,我们可以用多元线性回归模型来研究房屋售价与房屋面积、房间数量、地理位置等多个因素之间的关系。多元线性回归模型的数学描述如下:
h θ ( x ) = θ 0 x 0 + θ 1 x 1 + ⋯ + θ n x n = ∑ i = 0 n θ i x i = θ T X ( x 0 = 1 ) h_{\theta}(x)=\theta_{0}x_{0}+\theta_{1}x_{1}+\cdots+\theta_{n}x_{n}=\sum_{i=0}^{n}\theta_{i}x_{i}=\theta^{T}X(x_{0}=1) hθ(x)=θ0x0+θ1x1++θnxn=i=0nθixi=θTX(x0=1)

2.1 问题分析

问题描述:假设现在有一组数据,该数据描述了从 2008-2012 年上海的房价,现在请你根据该数据预测 2013 年的上海房价。

问题分析:房价预测问题就是一个典型的线性回归问题,我们的目的是建立一个数学模型来描述时间和房价之间的关系,由于自变量只有一个,因此这是一个一元线性回归模型,我们可以使用一条直线来对二者之间的关系进行拟合,即 y = k x + b y = kx + b y=kx+b

  • 特性1:若 b = 0 b = 0 b=0 y = k x y=kx y=kx 直线必过零点(模型表达能力会欠缺)
  • 特性2:当 x = 0 x = 0 x=0 时, y y y 值就是 b b b 即截距,而 k k k 是斜率(导数求斜率) k = d y d x = y 1 − y 0 x 1 − x 0 k = \frac{\mathrm{d} y}{\mathrm{d} x} = \frac{y_1-y_0}{x_1-x_0} k=dxdy=x1x0y1y0,若 x 1 = x 0 x_1=x_0 x1=x0 k k k 无意义即当直线与 y y y 轴垂直时 y y y 无意义,不能用 y = k x + b y=kx+b y=kx+b 表示

房价数据如下:

年份200820092010201120122013
价格6000800090001100015000?

可视化后如下:

在这里插入图片描述

图2-1 房价数据可视化

如何找到对应的直线呢?

我们可以先初始化一条直线 y = k x + b y=kx+b y=kx+b ,假设这就是我们要找的直线

如何衡量这条直线的好坏呢?

我们可以通过计算每个点到直线的距离,并求和取平均,使得总距离最短,如图 2-2 所示

如何去调整对应的直线使其能尽可能的符合预期呢?

我们需要根据总距离最短这个目的不断去更新直线的参数 k k k b b b

如何更新参数 k k k b b b

我们可以从求解 2 \sqrt 2 2 的例子中得到启发,定义损失函数 L L L 为总距离,利用梯度下降法 k k k b b b 沿着负梯度方向更新即可,如下所示,其中 l r lr lr 为学习率
k = k − l r ∗ ∂ L ∂ k k = k - lr * \frac{\partial L}{\partial k} k=klrkL

b = b − l r ∗ ∂ L ∂ b b = b - lr * \frac{\partial L}{\partial b} b=blrbL

在这里插入图片描述

图2-2 直线拟合房价数据

2.2 代码实现

我们先来简单实现,慢慢调试看有什么问题,代码如下:

import numpy as np
import matplotlib.pyplot as plt

data = np.array([
    [2008, 6000],
    [2009, 8000],
    [2010, 9000],
    [2011, 11000],
    [2012, 15000]
])


# 定义初始化的参数 k 和 b
k = np.random.random()
b = np.random.random()

X, Y = data.T

iter = 0    # 迭代次数
while True:
    
    iter += 1
    P = k * X + b   # 预测值
    L = np.mean(0.5 * (P - Y)**2)   # 定义损失函数

    print(f"{iter}. Loss: {L:.5f}, k = {k:.5f}, b = {b:.5f}")

    if L < 1e-5:    # 终止条件
        break

    delta_k = np.mean((P - Y) * X)  # 对 k 求偏导
    delta_b = np.mean(P - Y)        # 对 b 求偏导

    lr = 0.1
    k  = k - lr * delta_k   # k 更新
    b  = b - lr * delta_b   # b 更新

该代码实现了简单的线性回归预测,对一组房价和年份的数据进行拟合,以预测房价随时间的变化趋势。代码中首先随机初始化了 k k k b b b 两个参数,然后使用梯度下降算法不断迭代,通过损失函数计算预测值和真实值的误差,再根据误差反向传播来更新 k k k b b b,以期望使预测值更加接近真实值,最终得到一条直线,表示房价随时间的变化趋势。(from chatGPT)

输出如下:

1. Loss: 35297416.85023, k = 0.98189, b = 0.60411
2. Loss: 5000821078946638848.00000, k = 1573405.73810, b = 783.18369
3. Loss: 816251591822830492155922874368.00000, k = -635668580725.72083, b = -316252868.49308
4. Loss: 133231453522481572668586509982929722015744.00000, k = 256816018332930592.00000, b = 127769100099268.25000
...
59. Loss: nan, k = nan, b = nan
60. Loss: nan, k = nan, b = nan
61. Loss: nan, k = nan, b = nan
62. Loss: nan, k = nan, b = nan
63. Loss: nan, k = nan, b = nan
64. Loss: nan, k = nan, b = nan
65. Loss: nan, k = nan, b = nan
66. Loss: nan, k = nan, b = nan

可以看到直接跑飞了😂,可以看到一开始 L o s s Loss Loss 非常大,这意味着 Δ k \Delta k Δk Δ b \Delta b Δb 非常大,在下一次更新后 k k k b b b 非常大,最终没有迭代多少轮就放飞自我了。

根据输出结果,可以发现随着迭代次数的增加,损失函数的值呈现指数级别的增长,同时 k k k b b b 的值也变得异常巨大,最终导致损失函数的值变为 NaN。

我们可以将学习率 l r lr lr 设置的非常小来适配我们的 Δ k \Delta k Δk Δ b \Delta b Δb,但这似乎也不太好使,因为你不知道给多少合适,它本质上就是一个超参数,需要去调节,我们想固定 l r lr lr,能不能也得到一个比较好的效果呢?

当然是可以的,这个时候我们就要去限制 Δ k \Delta k Δk Δ b \Delta b Δb 了,而 Δ k \Delta k Δk Δ b \Delta b Δb 又是根据原始数据年份 X X X 和价格 Y Y Y 得来,那么我们可以考虑减小 X X X Y Y Y 的规模,对数据做缩放,让其在一个比较小的范围内

具体如何缩放呢?我们最容易想到的就是除以最大值,如下:

X = X / 2012
print(X)	# [0.99801193 0.99850895 0.99900596 0.99950298 1.        ]

但是又出现了新的问题,那就是缩放后的数据区分度太小了,所以我们对数据做预处理要满足两个条件:

  • 对数据做缩放,保证规模
  • 缩放时,确保数据具有区分性

我们可以使用最大最小值归一化方法

X = (X - X.min()) / (X.max() - X.min())
Y = (Y - Y.min()) / (Y.max() - Y.min())
print(X)	# [0.   0.25 0.5  0.75 1.  ]
print(Y)	# [0.         0.22222222 0.33333333 0.55555556 1.        ]

加入该方法后再来运行输出结果如下:

1. Loss: 0.32445, k = 0.93289, b = 0.75717
2. Loss: 0.24929, k = 0.89283, b = 0.67703
3. Loss: 0.19207, k = 0.85828, b = 0.60691
4. Loss: 0.14851, k = 0.82852, b = 0.54552
5. Loss: 0.11533, k = 0.80295, b = 0.49177
6. Loss: 0.09005, k = 0.78103, b = 0.44467
7. Loss: 0.07079, k = 0.76229, b = 0.40337
8. Loss: 0.05611, k = 0.74631, b = 0.36714
9. Loss: 0.04490, k = 0.73275, b = 0.33533
...

似乎没啥问题,也看不出来,我们来简单进行可视化下,代码如下所示:

import numpy as np
import matplotlib.pyplot as plt

data = np.array([
    [2008, 6000],
    [2009, 8000],
    [2010, 9000],
    [2011, 11000],
    [2012, 15000]
])


# 定义初始化的参数 k 和 b
k = np.random.random()
b = np.random.random()
X, Y = data.T

x_alpha = X.min()
x_beta  = X.max() - X.min()
y_alpha = Y.min()
y_beta  = Y.max() - Y.min()
X = (X - x_alpha) / x_beta
Y = (Y - y_alpha) / y_beta

iter = 0    # 迭代次数
while True:
    
    iter += 1
    P = k * X + b   # 预测值
    L = np.mean(0.5 * (P - Y)**2)   # 定义损失函数

    raw_x = X * x_beta + x_alpha    # x 复原
    raw_y = Y * y_beta + y_alpha    # y 复原
    raw_p = P * y_beta + y_alpha    # p 复原

    plt.cla()
    plt.xlim(2007, 2013)
    plt.ylim(0, 30000)
    plt.plot(X, Y, "b*")
    plt.plot(raw_x, raw_y, "b*")
    plt.plot(raw_x, raw_p, "r-")
    plt.pause(0.01)

    print(f"{iter}. Loss: {L:.5f}, k = {k:.5f}, b = {b:.5f}")

    if L < 1e-5:    # 终止条件
        break

    delta_k = np.mean((P - Y) * X)  # 对 k 求偏导
    delta_b = np.mean(P - Y)        # 对 b 求偏导

    lr = 0.1
    k  = k - lr * delta_k   # k 更新
    b  = b - lr * delta_b   # b 更新

可视化图如下所示:

在这里插入图片描述

图2-3 最大最小值归一化后直线拟合房价数据

好像是那么回事,我们再来分析下,最大最小值归一化是不是会出现 x = 0 x=0 x=0 y = 0 y=0 y=0 的情况,是不是意味着 y = k x + b y = kx+b y=kx+b 归零点,意味着 b = 0 b=0 b=0,我们可以看到输出中 b b b 是不断减小逼近于 0 的,因为减去 X m i n X_{min} Xmin Y m i n Y_{min} Ymin 相当于是把偏置 b b b 减掉了,相当于模型表达能力减弱了。

因此我们引入第二种归一化方法即正则化(对应正态分布)

  • X = ( X − m e a n ) / s t d X = (X - mean) / std X=(Xmean)/std
  • 能够更好更快的收敛,稳定性更强
  • 最大最小值归一化中当出现噪声点时(离散值),会严重影响归一化后的数据分布,稳定性差
x_alpha = X.mean()
x_beta  = X.std()
y_alpha = Y.mean()
y_beta  = Y.std()
X = (X - x_alpha) / x_beta
Y = (Y - y_alpha) / y_beta

可视化如下图:

在这里插入图片描述

图2-3 正则化后直线拟合房价数据

可以看到收敛更加稳定,对噪声带来的误差更小。

我们可以让其迭代到 200 次后退出循环,利用此时的 k k k b b b 做 2013 年的房价预测,代码如下所示:

import numpy as np
import matplotlib.pyplot as plt

data = np.array([
    [2008, 6000],
    [2009, 8000],
    [2010, 9000],
    [2011, 11000],
    [2012, 15000]
])


# 定义初始化的参数 k 和 b
k = np.random.random()
b = np.random.random()
X, Y = data.T

x_alpha = X.mean()
x_beta  = X.std()
y_alpha = Y.mean()
y_beta  = Y.std()
X = (X - x_alpha) / x_beta
Y = (Y - y_alpha) / y_beta

iter = 0    # 迭代次数
while True:
    
    iter += 1
    P = k * X + b   # 预测值
    L = np.mean(0.5 * (P - Y)**2)   # 定义损失函数

    raw_x = X * x_beta + x_alpha    # x 复原
    raw_y = Y * y_beta + y_alpha    # y 复原
    raw_p = P * y_beta + y_alpha    # p 复原

    plt.cla()
    plt.xlim(2007, 2013)
    plt.ylim(0, 30000)
    plt.plot(X, Y, "b*")
    plt.plot(raw_x, raw_y, "b*")
    plt.plot(raw_x, raw_p, "r-")
    plt.pause(0.01)

    print(f"{iter}. Loss: {L:.5f}, k = {k:.5f}, b = {b:.5f}")

    if L < 1e-5:    # 终止条件
        break

    delta_k = np.mean((P - Y) * X)  # 对 k 求偏导
    delta_b = np.mean(P - Y)        # 对 b 求偏导

    lr = 0.1
    k  = k - lr * delta_k   # k 更新
    b  = b - lr * delta_b   # b 更新
    
    if iter == 200:
        break

# 房价预测
year  = 2013
normalize_pred = k * (year - x_alpha) / x_beta + b
real_pred      = normalize_pred * y_beta + y_alpha
print(f"对于{year}年预测的房价是:{real_pred:.3f} 元")

输出如下:

对于2013年预测的房价是:16100.000

可以看到和我们猜想的预期是差不多的😀

2.3 总结

  • y = k x + b y = kx + b y=kx+b 能够表示一个线性模型
    • b = 0 b = 0 b=0 时,直线必定过 0 点,此时模型表达能力弱
    • b b b 是截距, k k k 是斜率,对于垂直于 y y y 轴的直线,无法表示
  • 对于 numpy 的矩阵,可以使用 x,y=data.T 在行方向上解包
  • 训练时,需要对数据进行预处理,目的是确保步长在比较小的范围内
  • 此时 l r lr lr 就相对比较固定,0.1 就行
  • 预处理的方法中,最大最小值归一化 x = (x - x.min()) / (x.max() - x.min())
  • 它会对异常数据比较敏感,不是一个好的做法
  • 预处理方法中,正则化:x = (x - x.mean()) / x.std()
  • 它考虑了所有数据的均值和方差,这是以后常见的方法,比较推荐

个人总结

本次课程正式进入人工智能的学习,首先我们从一个不同的角度复习了 sqrt 函数,然后我们讲解了利用线性回归来进行房价预测,利用直线模型来对数据进行拟合,要注意的是我们需要对数据进行预处理,否则步长在一个很大的范围,无法完成收敛,常见预处理包括最大最小值归一化和正则化,前者对数据比较敏感,后者考虑了所有数据的均值和方差,收敛性更好,值得推荐。

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

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

相关文章

Java开发手册-8

Java开发手册-8 MySQL 数据库SQL语句ORM映射 工程结构应用分层二方库依赖 MySQL 数据库 SQL语句 【强制】不要使用count(列名)或count(常量)来替代count(*)&#xff0c;count(*)是SQL92定义的标准统计行数的语法&#xff0c;跟数据库无关&#xff0c;跟NULL和非NULL无关。 说…

深入了解云计算:发展历程、服务与部署模型、未来趋势与挑战

开篇博主 bluetata 的观点&#xff1a;PaaS 服务必将是未来10年云计算权重最高的趋势&#xff08;05/02/2023 15:32&#xff09; 文章目录 一、前言二、认识了解云计算2.1 什么是云计算2.1.1 维基百科上的云计算定义2.1.2 NIST 标准云计算定义2.1.3 如果被面试如何解释云计算 2…

177_模型_Power BI 进销存6大日期维度期初与期末

177_模型_Power BI 进销存6大日期维度期初与期末 一、背景 在经销存报表设计中&#xff0c;经常会遇到的便是期初与期末。当然我们这里说期初与期末指的是期初库存与期末库存。 这里的期一般常见的会有&#xff1a;年月日。本案例将演示 6 大日期维度&#xff0c;分别是&…

(二)运行微信小程序:单页面和多页面

使用微信开发者工具创建项目后&#xff0c;默认将运行一个单页面的小程序&#xff0c;运行效果和文件对应修改处如下。 根据上图&#xff0c;通过修改对应内容&#xff0c;可以修改界面显示的文字。 这是一个单页面的小程序&#xff0c;假如要实现多页面的小程序&#xff0c;我…

基于matlab使用被动声纳系统定位声学信标

一、前言 此示例演示如何模拟被动声纳系统。固定的水声信标由浅水通道中的拖曳无源阵列检测和定位。声信标以每秒 10.37 千赫兹的速度传输 5 毫秒脉冲&#xff0c;并建模为各向同性投影仪。定位器系统在表面下方拖曳一个无源阵列&#xff0c;该阵列被建模为均匀线性阵列。一旦检…

电磁兼容三要素和三规律

EMC是业界的一个难点&#xff1b;来谈谈EMC三个规律、EMC问题三要素、电磁骚扰的特性、以及五层次EMC设计法。 EMC改进要如诊治疾病一样对症施治&#xff1b;我们倡导坚持EMC规律&#xff0c;趁早考虑和解决EMC问题-进行EMC设计。下面我们认识以下EMC领域的三个要 素和三个重要…

二十四、SQL 数据分析实战(12个简单的SQL题目)

文章目录 题目1: "双十一"活动的电商GMV分析题目2: 网站访问量分析题目3: 用户购物信息统计题目4: 连续售出的商品题目5: 奇偶互换位置题目6: 商品销量同环比题目7: 文本记录连接题目8: 行列互换题目9: 寻找符合要求的订单题目10: 优惠券使用分析题目11: 员工绩效考核…

国产仪器 3986A/3986D/3986E/3986F/3986H噪声系数分析仪

3986系列噪声系数分析仪产品包括3986A(10MHz&#xff5e;4GHz)、3986D(10MHz&#xff5e;18GHz)、3986E(10MHz&#xff5e;26.5GHz)、3986F(10MHz&#xff5e;40GHz)和3986H(10MHz&#xff5e;50GHz)&#xff0c;具有频率覆盖范围宽、频段选择灵活、接收灵敏度高、用户界面友好…

玩客云直刷armbian自带宝塔7.5

文章目录 前言一、短接玩客云1.1、流程1.2、短接操作 二、获取固件底包2.1、下载固件2.2、刷入成功后获取ip地址2.3、登陆2.4、其他 总结 前言 一开始25买了一个玩客云&#xff08;主机电源&#xff09;玩玩&#xff0c;成功刷入armbian&#xff0c;但是就是安装不了宝塔&…

MGV2000_2+16_当贝纯净桌面卡刷固件包-内有教程

MGV2000_216_当贝纯净桌面卡刷固件包-内有教程 特点&#xff1a; 1、适用于对应型号的电视盒子刷机&#xff1b; 2、开放原厂固件屏蔽的市场安装和u盘安装apk&#xff1b; 3、修改dns&#xff0c;三网通用&#xff1b; 4、大量精简内置的没用的软件&#xff0c;运行速度提…

什么是零拷贝?

零拷贝 什么是零拷贝 零拷贝指的是&#xff0c;从一个存储区域到另一个存储区域的copy任务无需CPU参与就可完成。零拷贝的底层是 通过DMA总线技术实现的。零拷贝与具体的编程语言无关&#xff0c;完全依赖于OS&#xff0c;OS支持就可使用&#xff0c;不支持 设置了也不起作用…

MySQL基础(二十二)逻辑架构

1.逻辑架构剖析 1.1 第1层&#xff1a;连接层 系统&#xff08;客户端&#xff09;访问MySQL服务器前&#xff0c;做的第一件事就是建立TCP连接。 经过三次握手建立连接成功后&#xff0c;MySQL服务器对TCP传输过来的账号密码做身份认证、权限获取。 用户名或密码不对&#…

单脉冲测角和差波束法原理

和差波束测角及仿真 和差波束法原理MATLAB仿真 和差波束法原理 和差波束法是等信号测角方法中的一种&#xff0c;该方法利用两个形状完全相同但是部分重叠的波束&#xff0c;两个波束再形成和波束和差波束&#xff0c;由和差波束测量目标回波的入射角。 如下图所示&#xff0c…

seL4 操作系统微内核生态-ACM协会

美国计算机协会 (ACM) 将 2022 年 ACM 软件系统奖项授予 seL4 微内核团队。 SeL4是世界上第一个通过数学方法被证明安全的操作系统内核&#xff0c;并且在安全的基础上还强调高性能&#xff0c;是世界上最快、最先进的 OS 微内核。它对于嵌入式计算系统的安全可信赖方面将会有极…

先人一步了解Go 1.21版本新特性前瞻

本文首发自「慕课网」&#xff0c;想了解更多IT干货内容&#xff0c;程序员圈内热闻&#xff0c;欢迎关注"慕课网"&#xff01; 作者&#xff1a;tonybai|慕课网讲师 正在如火如荼地开发当中&#xff0c;按照Go核心团队的一年两次的发布节奏来算&#xff0c;Go 1.21…

【Java入门合集】第四章继承(二)

博主&#xff1a;命运之光 专栏&#xff1a;JAVA入门 学习目标 1.掌握继承性的主要作用、实现、使用限制&#xff1b; 2.掌握this和super的含义及其用法&#xff1b; 3.掌握方法覆写的操作&#xff1b; 4.掌握final关键字的使用&#xff1b; 5.掌握类变量、实例变量和局部变量的…

Cyanine5 maleimide马来酰亚胺活化荧光染料Cy5;1437872-46-2

CY5-MAL细胞标记是一种用于标记细胞的荧光染料&#xff0c;它可以被用于多种应用中。首先&#xff0c;CY5-MAL细胞标记可以被用于活细胞成像。通过将CY5-MAL细胞标记与活细胞结合&#xff0c;可以使细胞在显微镜下清晰可见。这种技术可以被用于研究细胞的结构和功能。其次&…

linux彻底卸载mysql步骤

第一步&#xff0c;先查看是否安装了mysql mysql -u root -p 如果提示bash: mysql: command not found...则没有安装过mysql 如果提示需要输入密码&#xff0c;那就证明安装了mysql 第二步&#xff0c;查看mysql运行状态并关闭 先查看下mysql的运行状态&#xff08;如果已经…

redis(6)

基于redis中的list类型实现分页思路: list数据类型的应用场景: 1)对数据量大的集合做删减&#xff0c;比如说百度首页的热点新闻的列表&#xff0c;有一个换一换的功能&#xff0c;我们正是利用了list集合中的分页功能&#xff0c;使用lrange的命令&#xff0c;列表数据的显示&…

Anaconda——使用原因及创建方法

Anaconda——使用原因 一、使用原因二、创建虚拟环境的方法1、打开Anaconda Navigator2、点进Anaconda Powershell Prompt3、输入创建命令 参考文章 一、使用原因 Anaconda包含很多库Anaconda可以 创建虚拟环境 &#xff0c;满足不同工程的要求的工具版本不一致问题&#xff0…