深度学习中的优化算法

文章目录

  • 前言
  • 一、优化和深度学习
    • 1.1 优化的目标
    • 1.2 深度学习中的优化挑战
      • 1.2.1 局部最小值
      • 1.2.2 鞍点
      • 1.2.3 梯度消失
  • 二、梯度下降
    • 2.1 一维梯度下降
      • 2.1.1 学习率
    • 2.2 多元梯度下降
    • 2.3 自适应方法
      • 2.3.1 牛顿法
      • 2.3.2 其他自适应方法
  • 三、随机梯度下降
    • 3.1 随机梯度更新
    • 3.2 动态学习率
  • 四、小批量随机梯度下降
  • 五、动量法
  • 六、AdaGrad--自适应学习率
  • 七、Adam
  • 总结
  • 附录--参考资料


前言

本章我们主要讲解一下深度学习中的一些优化算法。

一、优化和深度学习

1.1 优化的目标

优化和深度学习的目标是根本不同的。前者主要关注的是最小化目标,后者则关注在给定有限数据量的情况下寻找合适的模型。

例如,训练误差和泛化误差通常不同:由于优化算法的目标函数通常是基于训练数据集的损失函数,因此优化的目标是减少训练误差。但是,深度学习(或更广义地说,统计推断)的目标是减少泛化误差。为了实现后者,除了使用优化算法来减少训练误差之外,我们还需要注意过拟合。

1.2 深度学习中的优化挑战

1.2.1 局部最小值

随着目标函数中的参数不断的迭代,目标函数的值可能陷入一个矩阵最小值,导致找不到目标函数的全局最小值。
在这里插入图片描述

在这种情况下,小批量上梯度的自然变化能够将参数从局部极小值中跳出。

1.2.2 鞍点

除了局部最小值之外,鞍点是梯度消失的另一个原因。鞍点(saddle point)是指函数的所有梯度都消失但既不是全局最小值也不是局部最小值的任何位置。

在二维情况下:
在这里插入图片描述

在三维情况下:
在这里插入图片描述
可以看到中间那个点是x方向的最小值,是y方向的最大值。

对于多维函数来说,判断一个该点是否是局部最小值、局部最大值还是鞍点。通常是通过海森矩阵的正定性来进行判断的。

一个多元函数的二阶泰勒展开式为:
f ( x ) = f ( a ) + ∑ i = 1 n f x i ( a ) ( x i − a i ) + 1 2 ∑ i = 1 n ∑ j = 1 n ( x i − a i ) ( x j − a j ) f x i x j ( a ) ] f(\mathbf{x}) = f(\mathbf{a}) + \sum_{i=1}^{n} f_{x_i}(\mathbf{a})(x_i - a_i) + \frac{1}{2} \sum_{i=1}^{n} \sum_{j=1}^{n} (x_i - a_i)(x_j - a_j) f_{x_i x_j}(\mathbf{a}) ] f(x)=f(a)+i=1nfxi(a)(xiai)+21i=1nj=1n(xiai)(xjaj)fxixj(a)]

改写成矩阵形式:
f ( x ) = f ( a ) + ( x − a ) T ▽ f ( a ) + 1 2 ( x − a ) T H ( x − a ) f(\mathbf{x}) = f(\mathbf{a}) + (\mathbf{x} - \mathbf{a})^T \bigtriangledown f(\mathbf{a}) + \frac{1}{2} (\mathbf{x} - \mathbf{a})^T \mathbf{H} (\mathbf{x} - \mathbf{a}) f(x)=f(a)+(xa)Tf(a)+21(xa)TH(xa)
其中H被称为海森矩阵。由于需要判断的点肯定是每个分量上的导数为0,所以上式可改写为:
f ( x ) = f ( a ) + 1 2 ( x − a ) T H ( x − a ) f(\mathbf{x}) = f(\mathbf{a}) + \frac{1}{2} (\mathbf{x} - \mathbf{a})^T \mathbf{H} (\mathbf{x} - \mathbf{a}) f(x)=f(a)+21(xa)TH(xa)
所以H的正定性决定了该点是什么样的点:

  • 当函数在零梯度位置处的Hessian矩阵的特征值全部为正值时,我们有该函数的局部最小值;

  • 当函数在零梯度位置处的Hessian矩阵的特征值全部为负值时,我们有该函数的局部最大值;

  • 当函数在零梯度位置处的Hessian矩阵的特征值为负值和正值时,我们有该函数的一个鞍点。

对于高维度问题,至少部分特征值为负的可能性相当高。这使得鞍点比局部最小值更有可能出现。

1.2.3 梯度消失

可能遇到的最隐蔽问题是梯度消失。导致梯度消失的一个经典原因就是激活函数的选择问题。例如tanh激活函数在两端的梯度接近于哦,导致优化将会停滞很长一段时间。
在这里插入图片描述

正如我们所看到的那样,深度学习的优化充满挑战。幸运的是,有一系列强大的算法表现良好,即使对于初学者也很容易使用。此外,没有必要找到最优解。局部最优解或其近似解仍然非常有用。

二、梯度下降

2.1 一维梯度下降

为什么梯度下降算法可以优化目标函数呢?我们可以先从一维梯度下降中观察。
首先一元函数在x点处的泰勒展开式为:
f ( x + ϵ ) = f ( x ) + ϵ f ′ ( x ) + O ( ϵ 2 ) . f(x + \epsilon) = f(x) + \epsilon f'(x) + \mathcal{O}(\epsilon^2). f(x+ϵ)=f(x)+ϵf(x)+O(ϵ2).
假设我们已知往负梯度方向可以减少f的值(这个可以去看梯度的推导),我们选择固定步长 η > 0 \eta > 0 η>0,然后取 ϵ = − η f ′ ( x ) \epsilon = -\eta f'(x) ϵ=ηf(x)。可得泰勒展开式: f ( x − η f ′ ( x ) ) = f ( x ) − η f ′ 2 ( x ) + O ( η 2 f ′ 2 ( x ) ) . f(x - \eta f'(x)) = f(x) - \eta f'^2(x) + \mathcal{O}(\eta^2 f'^2(x)). f(xηf(x))=f(x)ηf′2(x)+O(η2f′2(x)).
我们总可以找到足够小的 η \eta η使得高阶项变得无关,因此 f ( x − η f ′ ( x ) ) ⪅ f ( x ) . f(x - \eta f'(x)) \lessapprox f(x). f(xηf(x))f(x).
这时,我们使用 x ← x − η f ′ ( x ) x \leftarrow x - \eta f'(x) xxηf(x)来进行迭代x就能让f的值越来越小。

在这里插入图片描述

2.1.1 学习率

学习率(learning rate)决定目标函数能否收敛到局部最小值,以及何时收敛到最小值。这个学习率 η \eta η可由我们自己设定。

  • 如果设置过小,将导致x的更新非常慢,需要多次迭代。
    在这里插入图片描述
  • 如果设置的过大,泰勒展开式的高阶项可能会变大,导致函数的结果不来回波动,或者陷入局部最小值。
    在这里插入图片描述
    在这里插入图片描述

2.2 多元梯度下降

对于多元函数来说,它的在 x = [ x 1 , x 2 , … , x d ] ⊤ \mathbf{x} = [x_1, x_2, \ldots, x_d]^\top x=[x1,x2,,xd]的泰勒展开可以写为:
f ( x + ϵ ) = f ( x ) + ϵ ⊤ ∇ f ( x ) + O ( ∥ ϵ ∥ 2 ) . f(\mathbf{x} + \boldsymbol{\epsilon}) = f(\mathbf{x}) + \mathbf{\boldsymbol{\epsilon}}^\top \nabla f(\mathbf{x}) + \mathcal{O}(\|\boldsymbol{\epsilon}\|^2). f(x+ϵ)=f(x)+ϵf(x)+O(ϵ2).
其中梯度为: ∇ f ( x ) = [ ∂ f ( x ) ∂ x 1 , ∂ f ( x ) ∂ x 2 , … , ∂ f ( x ) ∂ x d ] ⊤ . \nabla f(\mathbf{x}) = \bigg[\frac{\partial f(\mathbf{x})}{\partial x_1}, \frac{\partial f(\mathbf{x})}{\partial x_2}, \ldots, \frac{\partial f(\mathbf{x})}{\partial x_d}\bigg]^\top. f(x)=[x1f(x),x2f(x),,xdf(x)].

我们同样可以使用 x ← x − η ∇ f ( x ) . \mathbf{x} \leftarrow \mathbf{x} - \eta \nabla f(\mathbf{x}). xxηf(x).来进行迭代。

等高图观察如下:
在这里插入图片描述

2.3 自适应方法

选择“恰到好处”的学习率 η \eta η是很棘手的。 如果我们把它选得太小,就没有什么进展;如果太大,得到的解就会振荡,甚至可能发散。
因此,我们可以考虑目标函数的值以及梯度和曲率的二阶方法,帮助我们自适应这个学习率。

2.3.1 牛顿法

一个多元函数f在x点 ϵ \epsilon ϵ领域内的泰勒展开:
f ( x + ϵ ) = f ( x ) + ϵ ⊤ ∇ f ( x ) + 1 2 ϵ ⊤ ∇ 2 f ( x ) ϵ + O ( ∥ ϵ ∥ 3 ) . f(\mathbf{x} + \boldsymbol{\epsilon}) = f(\mathbf{x}) + \boldsymbol{\epsilon}^\top \nabla f(\mathbf{x}) + \frac{1}{2} \boldsymbol{\epsilon}^\top \nabla^2 f(\mathbf{x}) \boldsymbol{\epsilon} + \mathcal{O}(\|\boldsymbol{\epsilon}\|^3). f(x+ϵ)=f(x)+ϵf(x)+21ϵ2f(x)ϵ+O(ϵ3).
我们令 H = d e f ∇ 2 f ( x ) \mathbf{H} \stackrel{\mathrm{def}}{=} \nabla^2 f(\mathbf{x}) H=def2f(x),其中 H \mathbf{H} H也被称为海森矩阵或者黑塞矩阵。

为了使得函数在这个领域内最小,我们需要令f的梯度为0,两边对 ϵ \epsilon ϵ求导,并忽略掉高阶项,得:
∇ f ( x ) + H ϵ = 0  and hence  ϵ = − H − 1 ∇ f ( x ) . \nabla f(\mathbf{x}) + \mathbf{H} \boldsymbol{\epsilon} = 0 \text{ and hence } \boldsymbol{\epsilon} = -\mathbf{H}^{-1} \nabla f(\mathbf{x}). f(x)+Hϵ=0 and hence ϵ=H1f(x).

所以我们迭代的时候直接令
x ← x − H − 1 ∇ f ( x ) x \leftarrow x -\mathbf{H}^{-1} \nabla f(\mathbf{x}) xxH1f(x)

但是我们需要注意一下,如果二阶导数为负值,即逼近曲线开头向下,f的值可能会增加。这是这个算法的致命缺陷!

这发生了惊人的错误。我们怎样才能修正它? 一种方法是用取Hessian的绝对值来修正,另一个策略是重新引入学习率。 这似乎违背了初衷,但不完全是——拥有二阶信息可以使我们在曲率较大时保持谨慎,而在目标函数较平坦时则采用较大的学习率

2.3.2 其他自适应方法

比如预处理、线搜索等。

三、随机梯度下降

3.1 随机梯度更新

在深度学习中,目标函数通常是训练数据集中每个样本的损失函数的平均值。给定n个样本的训练数据集,我们假设 f i ( x ) f_i(\mathbf{x}) fi(x)是关于索 i i i
的训练样本的损失函数,其中 x \mathbf{x} x是参数向量。然后我们得到目标函数:
f ( x ) = 1 n ∑ i = 1 n f i ( x ) . f(\mathbf{x}) = \frac{1}{n} \sum_{i = 1}^n f_i(\mathbf{x}). f(x)=n1i=1nfi(x).

可以看出反向传播的计算代价随着n线性增长,因此,当训练数据集较大时,反向传播代价高。

为了解决上述问题,我们提出了随机梯度下降(SGD)(注:我们常常把小批量梯度下降算法也称为SGD)。

随机梯度下降算法,不再计算完所有样本的损失函数再反向传播进行迭代了,而是随机抽取所有样本中的一个样本计算损失并反向传播更新参数

3.2 动态学习率

在随机梯度下降中,对学习率敏感,因此,我们可以采用动态调节学习率的方式改变学习率。以下是根据时间动态调整学习率的策略:
η ( t ) = η i  if  t i ≤ t ≤ t i + 1 分段常数 η ( t ) = η 0 ⋅ e − λ t 指数衰减 η ( t ) = η 0 ⋅ ( β t + 1 ) − α 多项式衰减 \begin{split}\begin{aligned} \eta(t) & = \eta_i \text{ if } t_i \leq t \leq t_{i+1} && \text{分段常数} \\ \eta(t) & = \eta_0 \cdot e^{-\lambda t} && \text{指数衰减} \\ \eta(t) & = \eta_0 \cdot (\beta t + 1)^{-\alpha} && \text{多项式衰减} \end{aligned}\end{split} η(t)η(t)η(t)=ηi if titti+1=η0eλt=η0(βt+1)α分段常数指数衰减多项式衰减

四、小批量随机梯度下降

随机梯度下降使用完整数据集来计算梯度并更新参数, 随机梯度下降算法一次处理一个训练样本来取得进展。 二者各有利弊:每当数据非常相似时,梯度下降并不是非常“数据高效”。 而由于CPU和GPU无法充分利用向量化,随机梯度下降并不特别“计算高效”。 这暗示了两者之间可能有折中方案,这便涉及到小批量随机梯度下降(minibatch gradient descent)。

加入损失函数的最终全貌图像如下:
在这里插入图片描述
假设我们当前参数所在位置在红点:
在这里插入图片描述
如果我们考虑了所有样本的损失函数的梯度变化情况,那么它就会找到当前点的最优梯度下降方向,最终情况可能如下:
在这里插入图片描述
每次方向找的是最优的,但是计算损失函数以及计算梯度太慢。

而如果随机找一个样本就更新参数,虽然迭代速度很快,但是过于随机可能需要很多次迭代才能降低。

在这里插入图片描述

而小批量梯度下降算法就是他们的折中,可参考下面代码:

lr = 0.03
num_epochs = 3
net = linreg
loss = squared_loss

for epoch in range(num_epochs):
    for X, y in data_iter(batch_size, features, labels):
        l = loss(net(X, w, b), y)  # X和y的小批量损失
        # 因为l形状是(batch_size,1),而不是一个标量。l中的所有元素被加到一起,
        # 并以此计算关于[w,b]的梯度
        l.sum().backward()
        sgd([w, b], lr, batch_size)  # 使用参数的梯度更新参数
    with torch.no_grad():
        train_l = loss(net(features, w, b), labels)
        print(f'epoch {epoch + 1}, loss {float(train_l.mean()):f}')

其中 l l l是一个向量,每个元素都是小批量中每个样本的损失,在本代码中我们使用了sum累加所有损失并反向传播,那么它会对每个样本的损失函数中的每一个参数求偏导并对应加到一起,当然我们除了使用sum也可以使用平均损失函数,pytorch中的MSELoss用的就是平均损失。在迭代器中,我们还需要去除batch_size对梯度的影响。如:

def sgd(params, lr, batch_size):  #@save
    """小批量随机梯度下降"""
    with torch.no_grad():
        for param in params:
            param -= lr * param.grad / batch_size
            param.grad.zero_()

五、动量法

之前我们都是把所有维度的梯度同时考虑并优化,那么我们能不能在每个维度上分别做不同程度的优化呢?答案是可行的,这就是我们要讲的动量法,考虑一种“惯性”。

我们可以先考虑两个参数的梯度下降优化。

在这里插入图片描述
我们可以看到它一方向的梯度大体是对的,而另一个轴的方向波动很大。
在这里插入图片描述
我们可以用当前点的梯度方向加上上个点的梯度方向,如果当前点的一个梯度分量与上个点的对应梯度分量方向相同,就会促进这个方向的变化,如果相反则会抑制它。

而动量法这是利用了这个特性,只不过动量法是考虑了前面所有的梯度并进行加权求和,而不是仅仅考虑上一个点。
在这里插入图片描述
这里的加权是用了指数加权平均法,这个我们在批量归一化那里也有说过。举个例子:
在这里插入图片描述

六、AdaGrad–自适应学习率

AdaGrad是一种优化算法,用于训练机器学习模型。它是基于梯度下降的一种自适应学习率方法,旨在解决传统梯度下降算法中学习率难以选择的问题。

AdaGrad通过自适应地调整学习率,可以在训练过程中逐渐减小对稀疏特征的学习率,而对频繁出现特征的学习率保持较大。具体而言,它会根据每个参数的历史梯度大小来动态地调整学习率。

公式如下:

$$$$

Adagrad优化算法就是在每次使用一个 batch size 的数据进行参数更新的时候,算法计算所有参数的梯度,那么其想法就是对于每个参数,初始化一个变量 s 为 0,然后每次将该参数的梯度平方求和累加到这个变量 s 上。

Adagrad特别适合稀疏数据,所谓的稀疏数据更多的是指某个特征有或者没有,而稠密数据是指都有这些特征,具体区别是特征数值的大小。

有这样一个规律,随着维度的增加,你遇到稀疏数据的可能性越高。

AdaGrad也有一个缺点,就是它会考虑前面所有的历史梯度,这样的话可能会对当前学习率的调整不够准确。改进的方法就是使用指数加权只考虑最近的一些历史梯度。----------此方法就叫做RMSprop方法

七、Adam

我们可以结合动量法与RMSprop方法,修正动量的同时,自适应学习率。

具体公式如下:
在这里插入图片描述

总结

附录–参考资料

  1. 各种优化算法总结
  2. 可视化软件

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

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

相关文章

了解Swarm 集群管理

Swarm 集群管理 简介 Docker Swarm 是 Docker 的集群管理工具。它将 Docker 主机池转变为单个虚拟 Docker 主机。 Docker Swarm 提供了标准的 Docker API,所有任何已经与 Docker 守护程序通信的工具都可以使用 Swarm 轻松地扩展到多个主机。 支持的工具包括但不限…

ubuntu服务器配置ftp服务

需求:配置ftp服务用于在windows电脑上直接浏览、下载、上传ubuntu服务器上的文件,用于文件共享,方便实用 效果:用户打开windows资源管理器后输入ftp://xxx.xxx.xxx.xxx (公网IP地址)后,即可浏览…

linux onlyOffice docker 离线部署

文章目录 前言1. 安装Docker容器2. 拉取镜像3. 验证 前言 docker 离线安装onlyoffice,如在线安装可直接跳过导出导入镜像步骤,拉取后直接运行。 1. 安装Docker容器 下载文件 wget https://download.docker.com/linux/static/stable/x86_64/docker-19…

在Linux中安装MySQL

在Linux中安装MySQL 检测当前系统中是否安装MySQL数据库 命令作用rpm -qa查询当前系统中安装的所有软件rpm -qa|grep mysql查询当前系统中安装的名称带mysql的软件rpm -qa | grep mariadb查询当前系统中安装的名称带mariadb的软件 RPM ( Red-Hat Package Manager )RPM软件包管理…

[golang gin框架] 45.Gin商城项目-微服务实战之后台Rbac微服务之角色权限关联

角色和权限的关联关系在前面文章中有讲解,见[golang gin框架] 14.Gin 商城项目-RBAC管理之角色和权限关联,角色授权,在这里通过微服务来实现角色对权限的授权操作,这里要实现的有两个功能,一个是进入授权,另一个是,授权提交操作,页面如下: 一.实现后台权限管理Rbac之角色权限关…

VSCode C/C++ 分目录编译配置

分目录编译配置记录 launch.json文件 注释处为修改内容 {// 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid830387"version": "0.2.0","configur…

【JavaEE进阶】Spring创建与使用

文章目录 一. 创建 Spring 项目1.1 创建一个Maven项目1.2 添加Spring依赖1.4. 创建一个启动类 二. 将 Bean 对象存放至 Spring 容器中三. 从 Spring 容器中读取到 Bean1. 得到Spring对象2. 通过Spring 对象getBean方法获取到 Bean对象【DI操作】 一. 创建 Spring 项目 接下来使…

嵌入式开发学习(STC51-9-led点阵)

内容 点亮一个点; 显示数字; 显示图像; LED点阵简介 LED 点阵是由发光二极管排列组成的显示器件 通常应用较多的是8 * 8点阵,然后使用多个8 * 8点阵可组成不同分辨率的LED点阵显示屏,比如16 * 16点阵可以使用4个8 *…

现代C++中的从头开始深度学习:【4/8】梯度下降

一、说明 在本系列中,我们将学习如何仅使用普通和现代C编写必须知道的深度学习算法,例如卷积、反向传播、激活函数、优化器、深度神经网络等。 在这个故事中,我们将通过引入梯度下降算法来介绍数据中 2D 卷积核的拟合。我们将使用卷积和上一个…

Linux6.32 Kubernetes kubeadm部署

文章目录 计算机系统5G云计算第三章 LINUX Kubernetes kubeadm部署一、kubeadm搭建 Kubernetes v1.20(一主两从)1.环境准备2.所有节点安装docker3.所有节点安装kubeadm,kubelet和kubectl4.部署K8S集群 二、kubeadm搭建 Kubernetes v1.20&…

没有进度管理的项目,都是在做无用功

在项目管理过程中,最大的挑战之一是确保项目实施与计划保持一致。 项目实施过程是一个相对漫长的过程,其中受到许多因素的影响。如果项目实施没有按照原始项目计划进行,很容易导致项目偏离计划,最终可能导致项目停滞或失败。 当…

vue+iviewUi+oss直传阿里云上传文件

前端实现文件上传到oss(阿里云)适用于vue、react、uni-app,获取视频第一帧图片 用户获取oss配置信息将文件上传到阿里云,保证了安全性和减轻服务器负担。一般文件资源很多直接上传到服务器会加重服务器负担此时可以选择上传到oss&…

Django基础

1.Django基础 路由系统视图模板静态文件和媒体文件中间件ORM(时间) 2.路由系统 本质上:URL和函数的对应关系。 2.1 传统的路由 from django.contrib import admin from django.urls import path from apps.web import viewsurlpatterns …

数据结构【哈夫曼树】

哈夫曼树 哈夫曼树的概念哈夫曼树的构造构造算法的实现哈夫曼树应用哈夫曼编码哈夫曼编码的算法实现 哈夫曼树的概念 最优二叉树也称哈夫曼 (Huffman) 树,是指对于一组带有确定权值的叶子结点,构造的具有最小带权路径长度的二叉树。权值是指一个与特定结…

【Hystrix技术指南】(6)请求合并机制原理分析

[每日一句] 也许你度过了很糟糕的一天,但这并不代表你会因此度过糟糕的一生。 [背景介绍] 分布式系统的规模和复杂度不断增加,随着而来的是对分布式系统可用性的要求越来越高。在各种高可用设计模式中,【熔断、隔离、降级、限流】是经常被使…

源码分析——LinkedList源码分析

文章目录 1.LinkedList简介2.内部结构分析3.LinkedList源码分析3.1构造方法3.2add方法3.3根据位置取数据的方法3.4根据对象得到索引的方法3.5检查链表是否包含某对象的方法: 1.LinkedList简介 LinkedList是一个实现了List接口和Deque接口的双端链表。 LinkedList底…

以技术驱动反欺诈,Riskified 为企业出海保驾护航

如今,全球对于线上消费的需求日益增长,各类新型支付方式也层出不穷。在国内,线上支付有着较为完善的法律及监管条例,格局基本已定型。但对于出海商家而言,由于不同国家和地区的支付规则和监管机制不同,跨境…

【PostgreSQL内核学习(十一)—— OpenGauss源码学习(CopyTo)】

可优化语句执行 概述什么是列存储?列存的优势 相关函数CopyToCStoreCopyToCopyStatetupleDescCStoreScanDesc CStoreBeginScanRelationSnapshotProjectionInfo GetCStoreNextBatchRunScanFillVecBatchCStoreIsEndScan CStoreEndScan 声明:本文的部分内容…

使用docker搭建GPT服务

不用ChatGPT账号,不用API,直接免费使用上官方原版的GPT4.0! 这个操作主要使用的是GitHub上的一个开源项目freegpt。 通过docker把这个项目打包到本地电脑上,直接就能使用上原版GPT4.0。 第一步:下载Docker 下载网址:docker.com 根据自己的电脑系统下载对应的版本即可 下…

虚拟ip地址软件哪个好 手机虚拟ip地址软件有哪些

虚拟ip地址修改器 IP转换器软件是一种用于把不同格式的IP地址转换为另一种格式的工具。下面是几种常见的深度IP转换器软件: 1. 深度IP转换器 深度IP转换器是一种收费的、简单易用的在线工具,可以将IPv4地址转换为16进制、2进制和10进制等格式。此外&am…