算法

一、卷积

卷积的基本含义

  • 本质上就是相乘求和
  • 功能上拥有数据过滤和增强作用
  • 对于相乘求和,是通用的使用卷积核每个像素点与对应的像素点相乘得到的结果求和作为中心点Result
  • 对于分类:在深度学习上分一维二维三维卷积
  • 一维卷积:卷积核是1*k的一维张量,被卷积对象是在平面维度也是1*W的一维张量,在总维度上一般是 [B,C,W] 三个维度
  • 二维卷积:卷积核是k1*k2,被卷积对象是平面维度H*W,在总维度是 [B,C,H,W] 三个维度

Pytorch卷积的参数

  • 对于参数:需要设定的是输入通道,输出通道(卷积核个数),卷积核大小,Padding,Stride,dilation,bias
  • 输入通道和输出通道与卷积核个数关系:
    • 输入通道为n总维度(B,N,H,W),卷积核个数为m(大小为K*K,拥有N*K*K个W0权重(用于对不同通道的输入进行乘法)),则每一个卷积核会对所有n个通道都进行卷积得到feature map,这个feature map也是n个通道的;但是最终结果需要一个卷积核对应一个通道,那么就必须将这n个通道融合成一个通道,因此,先进行累加得到一个数组,如果有Bias则会对每一通道数据加上一个偏置Bias(一个卷积核只有一个,是一个数字),最后得到最终的输出通道数据(B,1,H,w),那么m个卷积核就可以得到(B,M,H,W)的输出
  • 卷积核大小:一维(1*k),二维(k1*k2),三维(k1*k2*k3);每一个位置都是C(输入通道数)个权重W0,计算时,将权重与对应像素点相乘得到的结果相加作为中心像素的输出
  • Padding:一维(1*p),二维(p1*p2),三维(p1*p2*p3);补全,在维度上进行补全,一般补0,可以自定义
  • Stride:一维(1*s),二维(s1*s2),三维(s1*s2*s3);步长,每次卷积核大小的乘法和计算完毕移动到下一个区域时,要根据步长移动相应的距离,默认1,可改
  • 输入输出尺寸变化(通用):
W_{out} = (W_{in}+2p-m)/s+1
  • dilation:空洞卷积

卷积的参数量计算量

  • 参数量与卷积核的数据相关,与被卷积对象无关,参数量为:
(C_{out}(卷积核个数) * K_1 * K_2 C_{in} + C_{out}(加上卷积核个数的偏置Bias))
  • 备注:

    • 首先Cout * K1 * K2 毫无疑问,含有这些个数的卷积容器
    • 每个容器包含Cin个权重,因为在卷积过程中,一个卷积核需要对不同输入通道进行处理,要给每个通道处理过程赋不同的权重,所以这里 * Cin
    • 最后,如果有Bias,则每个卷积核需要存储一个偏置用于计算后的修正
  • 对于每一个像素点的计算过程需要(Cin * K1 * K2)的乘法计算过程,再加上(Cin * K1 * K2 - 1)的累加计算过程,如果含Bias则需要在进行一次+Bias的过程,因此加法过程也是(Cin * K1 * K2)次,两者和是 (2 * Cin * K1 * K2)(不考虑Bias则少一次 (2 * Cin * K1 * K2 - 1)
  • 此时每个像素点计算完成,总共需要计算(Cout * H * W)个像素点
  • 因此最终Conv2D计算次数:
M = 2 * C_{out} * H * W * ( C_{in} * K_1 * K_2 )
  • 同理可得Conv3D计算量:
M = 2 * C_{out} * H * W * T * ( C_{in} * K_1 * K_2 * K_3 )
  • 同理可得Conv1D计算量:
M = 2 * C_{out} W * ( C_{in} * K )

二、激活函数

为什么有激活函数

  • 神经网络中如果不加非线性变换,那都是线性函数(没有曲率),网络学习不到复杂的维度,应该添加非线性变换,激活函数本身是一个非线性的,添加它能使网络能够学习到更高维度的事物,学习能力更强。
  • 大部分激活函数具有归一化作用,限定在一定的区间
  • 可以过滤掉一些不需要的数据

激活函数类别

Sigmoid

f(x)= \frac{1}{1+e^{-x}}
  • 曲线:小于0为0,大于0为1,等于0为0.5;将数据限制在[0,1]
  • 优点:
    • 处处连续,便于求导
    • 数值的范围压缩到[0,1]
  • 缺点:
    • 激活函数计算量大(指数运算)
    • 反向传播时,很容易就会出现梯度消失(求导会让仅仅在0左右存在梯度,其他处会梯度几乎为0)
    • 中心点不为0,导致收敛缓慢,因为数值与权重相乘时,有时需要的效果需要某两个值符号相反,但是Sigmoid无法进行这种操作,就会导致多次同符号相乘来逼近收敛值(Sigmoid提供两个正值,其他层激活函数提供两个负值,逐步逼近收敛值)

tanh

f(x)= \frac{2}{1+e^{-2x}}-1 = \frac{e^{x}-e^{-x}}{e^{x}+e^{-x}}
  • 曲线:类似于Sigmoid,但是中间点在原点;小于0为-1,大于0为1,等于0为0;将数据限制在[-1,1]
  • 优点:与Sigmoid相同,并且数据压缩到[-1,1]
  • 缺点:与Sigmoid相同,但优化了中心点不为0的问题

ReLU

f(x)=\begin{cases} x &x>=0\\0 &x<0
\end{cases}
  • 曲线:大于0的大于0,小于等于0的等于0,曲线是线性的,求导为0和1;
  • 优点:
    • 当输入为正时,不存在梯度饱和问题
    • 没有指数运算,计算量小
  • 缺点:
    • 神经元坏死(梯度为0,这一层权重永远不更新)

LeakyReLU

f(x)=\begin{cases} x &x>=0\\\alpha x &x<0
\end{cases}
  • 曲线:大于0的大于0,等于0的等于0,小于0的小于0,曲线是线性的,求导为,α和1;
  • 优点:与ReLU一样,解决了ReLU的坏死问题
  • 缺点:结果不一致,无法为正负输入值提供一致的关系预测(不同区间函数不同)

ELU

f(x)=\begin{cases} x &x>=0\\\alpha(e^{x}-1) &x<0
\end{cases}
  • 曲线:大于0的大于0,等于0的等于0,小于0的小于0,曲线是线性的,求导为,α和1;
  • 优点:
    • 与LeakyReLU类似,但是将小于0部分改成了指数函数
    • 正常梯度更接近于单位自然梯度,从而使均值向零加速学习
    • 较小的输入下会饱和至负值,从而减少前向传播的变异和信息
  • 缺点:有指数,计算量大;仍有LeakyReLU的缺点

归一化函数

为什么要归一化

ICS(internal covariate shift)问题:多层之间数据需求和供给差距太大

归一化优点

  • 一定程度提高模型精度(解决ICS问题)
  • 提升收敛速度(解决ICS问题)

Softmax

f(x)= \frac{e^{x_i}}{\sum_j^n e^{x_j}}
  • 描述:一堆实数的值映射到0-1区间,并且使他们的和为1,每个数表示为他的权重与总体权重的比例

Batch Normalization

  • 描述:对[B,C,H,W]的矩阵的每个C作为一组(通道组),对组内归一化(求均值,求方差),总共C组(保留C的信息,将B,H,W的信息进行过滤)
  • 作用:
    • 防止过拟合,可以用来替代drop out层
    • 可以防止梯度弥散
    • 其能够让优化空间(optimization landscape)变的平滑
  • 训练时,得到γ和β,这是BN的可学习参数,测试通用;另外得到均值和方差
  • 测试时会固定训练得到的全局均值和标准差
  • 对比:
    • 在总的层面上,比Dropout更好,适用性更高
    • BN在CV方向处理用的多(保留C信息),LN在NLP用的多,保留B信息
Dropout
  • 解决问题:过拟合(也就是在测试集上的精度很低)
  • 描述:在正向传播过程每个神经元有一定几率停止工作
    • 训练过程:

      • 随机临时地删除一部分神经元(即每个神经元以一定的概率使得其激活函数的输出为0),这些被删除的神经元的权重要备份起来。
      • 用保留的神经元进行前向传播得到误差,采用反向传播对这些保留的神经元进行参数更新,临时删除的不更新。
      • 恢复被删除的神经元
    • 测试过程:采用全部神经元,但每一个神经单元的权重参数要乘以概率p

Layer Normalization

  • 描述:对[B,C,H,W]的矩阵的每个B作为一组(通道组),对组内归一化(求均值,求方差),总共B组

Instance Normalization

  • 描述:对[B,C,H,W]的矩阵的每个C作为一组(通道组),对组内归一化(求均值,求方差),总共C*B组

Group Normalization

  • 描述:对[B,C,H,W]的矩阵的每个C/h作为一组(通道组),对组内归一化(求均值,求方差),总共h*B组

训练测试相关

反向传播

  • 本质上就是求导,结果传给下一层,卷积就是对w权重求导,对b偏置求导,激活函数对激活函数本身求导
  • 每一层的梯度越大,那么权重更新的越大,学习速度越快

梯度消失和梯度爆炸

  • 深层网络容易出现,ResNet解决了梯度消失问题(直接add就不存在梯度会消失了)
  • 梯度消失:就是梯度无限接近于0,相乘导致结果趋于0
  • 梯度爆炸:连续的梯度大于1,相乘导致结果趋于无穷
  • 梯度饱和:在某个区间,梯度变化会非常小且接近于0,如Sigmoid,tanh

过拟合和欠拟合

  • 欠拟合是指模型在训练集、验证集和测试集上均表现不佳的情况;

    • 原因

      • 模型复杂度过低
      • 特征量过少
    • 解决
      • 模型复杂化
      • 增加更多的特征,使输入数据具有更强的表达能力
      • 调整参数和超参数
      • 降低正则化约束
  • 过拟合是指模型在训练集上表现很好,到了验证和测试阶段就很差,即模型的泛化能力很差。
    • 原因

      • 建模样本选取有误,如样本数量太少,选样方法错误,样本标签错误等,导致选取的样本数据不足以代表预定的分类规则
      • 样本噪音干扰过大,使得机器将部分噪音认为是特征从而扰乱了预设的分类规则
      • 假设的模型无法合理存在,或者说是假设成立的条件实际并不成立
      • 参数太多,模型复杂度过高
      • 对于决策树模型,如果我们对于其生长没有合理的限制,其自由生长有可能使节点只包含单纯的事件数据(event)或非事件数据(noevent),使其虽然可以完美匹配(拟合)训练数据,但是无法适应其他数据集
      • 对于神经网络模型:a)对样本数据可能存在分类决策面不唯一,随着学习的进行,,BP算法使权值可能收敛过于复杂的决策面;b)权值学习迭代次数足够多(Overtraining),拟合了训练数据中的噪声和训练样例中没有代表性的特征
    • 解决
      • 增加训练数据数
      • 使用正则化约束
      • 减少特征数
      • 调整参数和超参数
      • 降低模型的复杂度
      • 使用Dropout
      • 提前结束训练

优化器

  • 优化器是根据loss进行反向传播,更新网络中的权重,使得网络逼近以至得到最优解的算法
  • PyTorch.optimizer.zero_grad():每个batch结束给梯度归零
  • PyTorch.optimizer.step():更新网络参数
W,\theta_t = 权重\\
\frac{\partial loss}{\partial W},g_t = 梯度\\
\eta = 学习率lr\\
diag = 对角矩阵化\\
G_{t,i} = 每处梯度的平方和的矩阵化表示

SGD随机梯度下降(BGD,MBGD)

\theta_t =\theta _{t-1}-\eta g_t
  • 每次更新度随机采用一个样本计算损失来更新参数,计算比较快,占用内存小,可以随时新增样本。这种方式对于样本中的异常数据敏感,损失函数容易震荡。容易收敛到局部极小值,但由于震荡严重,会跳出局部极小,从而寻找到接近全局最优的解。
  • 代码中lr为SGD中的随机步长,但在实际应用中,lr一般在局部是固定的;grads是梯度,params是参数权重
  • 梯度的方向并没有指向最小值的方向,呈之字形缓慢趋向最小值
for key in params.keys():params[key] -= self.lr * grads[key]
  • SGD,BGD,MBGD区别

    • BGD(批量梯度下降):更新每一参数都用所有样本更新,m=all,更新100次遍历多有数据100次
    • SGD(随机梯度下降):更新每一参数都随机选择一个样本更新,m=1
    • MBGD(小批量梯度下降):更新每一参数都选m个样本平均梯度更新,1<m<all
    • 总结:SGD训练速度快,大样本选择;BGD能得到全局最优解,小样本选择;MBGD综合二者选择。

Momentum(SGDM,SGD with momentum)

Momentum

v_t =\beta v_{t-1} - \eta g_t \\
\theta_t =\theta _{t-1}+v

SGDM

v_t = \beta v_{t-1} - (1-\beta)\eta g_t\\
\theta_t =\theta _{t-1}+v
  • α表示动量因子,一般取值0.9,也可以理解为衰减因子,类似于在地面上滚动小球时,摩擦力和空气阻力所带来的的影响。v表示速度,表示物体在梯度作用力下的速度
  • 每次梯度更新都会带有前几次梯度方向的惯性,使梯度的变化更加平滑;Momentum梯度下降算法能够在一定程度上减小权重优化过程中的震荡问题。
  • 优点:使得梯度方向不变的维度上速度变快,梯度方向有所改变的维度上的更新速度变慢,这样就可以加快收敛并减小震荡
  • 缺点:无法预知,从而预先减速;不能根据参数的重要性而对不同的参数进行不同程度的更新(一个网络所有的参数都一视同仁,使用同样的一个整体处理方式)。
for key in params.keys():self.v[key] = self.alpha* self.v[key] - self.lr * grads[key]params[key] += self.v[key]

NAG(Nesterov Accelerated Gradient 牛顿动量梯度下降)

v_t = \beta v_{t-1} - (1-\beta)\frac{\partial loss}{\partial(W-\eta\alpha v)}\\
\theta_t =\theta _{t-1}+v
  • NGA算法首先得到当前点的动量项,之后得到提前点的梯度方向,用两者结合得到当前点的梯度更新方向。
  • 优点:解决了SGDM的无预知性
  • 缺点:不能根据参数的重要性而对不同的参数进行不同程度的更新。

AdaGrad

v_t = G_{t,i} = diag(\sum_{i-1}^{t}Grads_{i,1}^2,\sum_{i-1}^{t}Grads_{i,2}^2,...,\sum_{i-1}^{t}Grads_{i,d}^2)\\
\theta_t =\theta _{t-1} - \frac{\eta}{\sqrt{v_t+\epsilon}}\frac{\partial loss}{\partial W}
  • 防止分母为0,加了一个随机扰动ε
  • 优点:无需手动调整梯度
  • 缺点:因为h一直在增大,所以到了后期,W的变化将会逐渐变小;初期分母小,梯度变化大,后期分母大,梯度变化小,会很快学习结束,可能会出现还未收敛到最小值的情况

RMSProp、Adadelta

v_t = \beta v_{t-1} + (1-\beta)G_{t,i}\\
\theta_t =\theta _{t-1} - \frac{\eta}{\sqrt{v_t+\epsilon}}\frac{\partial loss}{\partial W}
  • 解决Adagrad分母会不断积累,这样学习率就会收缩并最终会变得非常小的问题。RMSprop在Adagrad算法的基础上对η进行了一阶指数平滑处理,,学习率系数的分母部分不再是单纯的累加,变成了滑动加权平均值,不会导致学习率消失的问题。

Adam

m_t=\beta _1m_{t-1}+(1-\beta _1)g_t\\
v_t=\beta _2v_{t-1}+(1-\beta _2)g_t^2\\
\hat{m_t}=\frac{m_t}{1-\beta _1^t}\\
\hat{v_t}=\frac{v_t}{1-\beta _2^t}\\
\theta_t =\theta _{t-1}-\eta \frac{\hat{m}}{\sqrt{\hat{v}+\epsilon} }
  • 其中β1默认值为0.9,β2默认值为0.999,ε为10^-8,Adam集合动量和Adadelta两者的优点

Adamax

m_t=\beta _1m_{t-1}+(1-\beta _1)g_t\\
v_t=\beta^\infty_2 v_{t-1}+(1-\beta^\infty_2)|g_t|^\infty\\
\hat{m_t}=\frac{m_t}{1-\beta _1^t}\\
u_t=\sqrt[\infty]{v_t} = max(\beta_2*v_{t-1}, |g_t|)\\
\theta_t =\theta _{t-1}-\eta \frac{\hat{m}}{u_t}
  • 因为ut大于0,所以无需ε,只需要两个β就行

Nadam

m_t=\beta _1m_{t-1}+(1-\beta _1)g_t\\
\hat{m_t}=\frac{m_t}{1-\beta _1^t}\\
\theta_t =\theta _{t-1}-\eta \frac{1}{\sqrt{\hat{v}+\epsilon}}(\beta_1\hat{m}+\frac{(1-\beta_1)g_t}{1-\beta_1^t})
  • Nadam是adam和NAG结合;不再像NAG提前预测后面位置,而是直接在当前位置对当前梯度方向做两次更新,同样运用到Adam中需要对m做一个修正

AMSGrad

m_t=\beta _1m_{t-1}+(1-\beta _1)g_t\\
v_t=\beta _2v_{t-1}+(1-\beta _2)g_t^2\\
\hat{v_t} = max(v_t,v_{t-1})\\
\theta_t =\theta _{t-1}-\eta \frac{\hat{m}}{\sqrt{\hat{v}+\epsilon} }
  • 标识别、机器翻译领域,自适应学习速率的方法无法收敛到最佳情况,并且弱于基于动量的随机梯度下降

总结


Loss

  • 回归损失:平均绝对误差(MAE/L1损失),平均平方误差(MSE/L2损失),smooth L1 loss,Huber损失,log cosh loss,quantile loss;
  • 分类损失:0-1损失,logistic loss(对数损失),hinge loss(铰链损失),exponential loss(指数损失),KL散度;
  • 识别、检测和分割常用的损失:softmax cross-entropy loss,weighted cross-entropy loss,focal loss,OHEM,center loss,triplet loss,contrastive loss,L-softmax,LMCL,IOU loss,GIOU loss,DIOU loss,CIOU loss,dice loss。
  • 正则化(Regularization)是机器学习中对原始损失函数引入额外信息,以便防止过拟合和提高模型泛化性能的一类方法的统称。将目标函数变成 原始损失函数+额外项

MAE/L1

MAE = \frac{1}{n}\sum_{i=1}^n|f(x_i)-y_i|
  • 对估计值和真实值之差取绝对值的平均值
  • 曲线连续但是在f(x)=y时不可导,求解效率低
  • 梯度较为稳定,但是即使损失很小梯度仍然保持不变,不利于模型的收敛
  • 对异常值更鲁棒(相比于L2)。
  • 正则化:但是可以利用L1进行正则化;保持解的稀疏性,即为了使损失最小化,一些影响小的权重参数经过学习设置为0,可以用于特征选择

MSE/L2

MSE = \frac{1}{n}\sum_{i=1}^n{(f(x_i)-y_i)}^2

PSNR:

PSNR = 10log_{10}(\frac{{255}^2}{MSE})
  • 对估计值和真实值之差取平方和的平均值
  • MSE是平滑函数、处处可导,因此在求解优化问题时有利于误差梯度的计算
  • 随着误差的减小,梯度也在减小,因此使用固定的学习速率,也能较快的收敛到最小值
  • 通过平方计算放大了估计值和真实值的距离,因此对于异常值带来很大的惩罚,从而降低正常值的预测效果
  • 误差很大时梯度也很大,在训练初期不稳定,容易梯度爆炸
  • 正则化:L2进行正则化可以防止过拟合,在正则化后的梯度下降迭代公式中,会给权重参数乘以一个小于1的因子;权重值减小,从而得到的模型越平滑

Charbonnier Loss

CharbonnierLoss = \sqrt{{(y-f(x))}^2+\epsilon^2}
  • L1和L2会造成图像过于平滑的问题,缺乏感官上的照片真实感
  • 优点是曲线更平滑了,接近零点的值的梯度由于常数隆的存在,梯度也不会太小,避免梯度消失;远离零点的值的梯度由于开方,梯度也不会太大,避免梯度爆炸。

crossentropy 交叉熵

C = \frac{1}{n}\sum_{i=1}^{n}{[Yln(P)+(1-Y)ln(1-P)]}
  • 其中1的概率是P,0的概率是(1-P)

深度学习-CV方向基本原理相关推荐

  1. 【深度学习】煮酒论英雄:深度学习CV领域最瞩目的top成果总结

    前言 如果06年Hinton的深度置信网络是深度学习时代的开启,12年的Alexnet在ImageNet上的独孤求败是深度学习时代的爆发,那么至今也有近15年的发展历程.15年足够让一个青涩懵懂的少年 ...

  2. ckpt下载 deeplabv3_煮酒论英雄:深度学习CV领域最瞩目的成果top46

    原标题:煮酒论英雄:深度学习CV领域最瞩目的成果top46 来源:Smarter 作者:皮特潘 [新智元导读]本文盘点深度学习CV领域杰出的工作,从基础研究.分类骨架.语义分割.实例分割.目标检测.生 ...

  3. 「每周论文推荐」 初入深度学习CV领域必读的几篇文章

    https://www.toutiao.com/a6718570271269192200/ 很多朋友都希望我们开通论文推荐和阅读板块,那就开吧,此专栏名为<每周论文推荐>.在这个专栏里,还 ...

  4. 有三AI发布360页11万字深度学习CV算法工程师成长指导手册,可下载收藏打印,未完待续...

    文/编辑 | 言有三 字少事大,各位同学,今天有三AI平台发布<深度学习视觉算法工程师成长指导手册>,超过11万字,360页word文档,可下载收藏打印,且还有大约1/3内容并未完结,最终 ...

  5. 【每周论文推荐】 初入深度学习CV领域必读的几篇文章

    很多朋友都希望我们开通论文推荐和阅读板块,那就开吧,此专栏名为<每周论文推荐>.在这个专栏里,还是本着有三AI一贯的原则,专注于让大家能够系统性完成学习,所以我们推荐的文章也必定是同一主题 ...

  6. 深度学习CV领域必读论文

    深度学习CV领域必读论文 01 深度学习CV领域划时代论文具有里程碑意义 期刊日期: NIPS-2012,Alexnet 论文名称: <ImageNet Classification with ...

  7. 深度学习(计算机视觉方向)小白入门的一些建议

    点击上方"视学算法",选择"星标" 快速获得最新干货 作者:大连海事大学计算机硕士:阿小博Dennis https://zhuanlan.zhihu.com/p ...

  8. 2021年深度学习哪些方向比较有研究潜力?

    来源:知乎问答 作者:陀飞轮.Zhifeng.谢凌曦 转自:极市平台 深度学习成为近些年来较为热门的领域,算法工程师这一岗位也变得越发的抢手,尽管已经踏入了这一领域但对整体的大环境其实是还不能够准确的 ...

  9. 【深度学习】2021年深度学习哪些方向比较新颖,处于上升期或者朝阳阶段,没那么饱和,比较有研究潜力?...

    先写两个最近火热我比较看好的方向Transformer和Self-Supervised,我这里举的例子倾向于计算机视觉方向.最后再补充Zero-Shot和多模态两个方向. 1.Transformer ...

最新文章

  1. Docker的安装、镜像源更换与简单应用
  2. C#中的线程(二) 线程同步基础
  3. swift 函数篇章
  4. 每天研究一个产品,阿德老师“手摸手”带你写产品分析报告 |
  5. 【CV】语义分割:最简单的代码实现!
  6. export default (imported as router) was not found_小学生必须知道的英语语法知识:as…as的七大用法...
  7. 数组竟然可以这样定义
  8. NYOJ-01串(dp)
  9. 无人机-2多翼无人机的结构与硬件
  10. 解决win10 蓝牙设备只能配对无法连接 ,并且删除设备无效的问题
  11. 好戏常有:CSW和BU撕逼,Cobra劝架
  12. Unity UGUI优化:解决EventSystem耗时过长的问题 第一部分
  13. 给你的页面加一个百度地图
  14. Redis客户端与服务端
  15. manjaro KDE 安装微信
  16. php 站内信 类
  17. 致家长--为什么选择Scratch
  18. 向mysql写入时间_Python向Mysql写入时间类型数据
  19. DICOM医学图像处理:DICOM存储操作之 “多幅JPG图像数据存入DCM文件”
  20. 卡西欧计算机fx82cnx怎么玩游戏,卡西欧fx-82ES计算器乱码玩法问题

热门文章

  1. [学习] 09 一小时读100页的快速阅读法
  2. 【北邮人福利】微软office和windows完全免费了
  3. 实战getshell新姿势-SSRF漏洞利用与getshell实战
  4. 关于止损和资金管理,变种凯利公式与我的投资新观点
  5. MySQL实验7 数据库的安全性
  6. 计算机职场办公软件,作为一个职场人,电脑上必备哪些软件?
  7. yum软件仓库的配置详解
  8. Go 中的循环依赖检测工具 —— go-cyclic
  9. 字节跳动取消大小周,程序员们有何意见?
  10. Python数据分析及可视化实例之“Pandas“