Deep Compression阅读理解及Caffe源码修改

2023-05-16

更新:
没想到这篇文章写出后有这么多人关注和索要源码,有点受宠若惊。说来惭愧,这个工作当时做的很粗糙,源码修改的比较乱,所以一直不太好拿出手。最近终于有时间整理了一下代码并开源出来了。关于代码还有以下几个问题:
~1.在.cu中目前仍然是调用cpu_data接口,所以可能会增加与gpu数据交换的额外耗时,这个不影响使用,后面慢慢优化。~(已解决)
2.目前每层权值修剪的比例仍然是预设的,这个比例需要迭代试验以实现在尽可能压缩权值的同时保证精度。所以如何自动化选取阈值就成为了后面一个继续深入的课题。
3.直接用caffe跑出来的模型依然是原始大小,因为模型依然是.caffemodel类型,虽然大部分权值为0且共享,但每个权值依然以32float型存储,故后续只需将非零权值及其下标以及聚类中心存储下来即可,这部分可参考作者论文,写的很详细。
4.权值压缩仅仅压缩了模型大小,但在前向inference时不会带来速度提升。因此,想要在移动端做好cnn部署,就需要结合小的模型架构、模型量化以及NEON指令加速等方法来实现。
代码开源在github
https://github.com/may0324/DeepCompression-caffe

最近又转战CNN模型压缩了。。。(我真是一年换N个坑的节奏),阅读了HanSong的15年16年几篇比较有名的论文,启发很大,这篇主要讲一下Deep Compression那篇论文,因为需要修改caffe源码,但网上没有人po过,这里做个第一个吃螃蟹的人,记录一下对这篇论文的理解和源码修改过程,方便日后追本溯源,同时如果有什么纰漏也欢迎指正,互相交流学习。
这里就从Why-How-What三方面来讲讲这篇文章。

Why

首先讲讲为什么CNN模型压缩刻不容缓,我们可以看看这些有名的caffe模型大小:
1. LeNet-5 1.7MB
2. AlexNet 240MB
3. VGG-16 552MB
LeNet-5是一个简单的手写数字识别网络,AlexNet和VGG-16则用于图像分类,刷新了ImageNet竞赛的成绩,但是就其模型尺寸来说,根本无法移植到手机端App或嵌入式芯片当中,就算是想通过网络传输,较高的带宽占用率也让很多用户望尘莫及。另一方面,大尺寸的模型也对设备功耗和运行速度带来了巨大的挑战。随着深度学习的不断普及和caffe,tensorflow,torch等框架的成熟,促使越来越多的学者不用过多地去花费时间在代码开发上,而是可以毫无顾及地不断设计加深网络,不断扩充数据,不断刷新模型精度和尺寸,但这样的模型距离实用却仍是望其项背。
在这样的情形下,模型压缩则成为了亟待解决的问题,其实早期也有学者提出了一些压缩方法,比如weight prune(权值修剪),权值矩阵SVD分解等,但压缩率也只是冰山一角,远不能令人满意。今年standford的HanSong的ICLR的一篇论文Deep Compression: Compressing deep neural networks with pruning, trained quantization and Huffman coding一经提出,就引起了巨大轰动,在这篇论文工作中,他们采用了3步,在不损失(甚至有提升)原始模型精度的基础上,将VGG和Alexnet等模型压缩到了原来的35~49倍,使得原本上百兆的模型压缩到不到10M,令深度学习模型在移动端等的实用成为可能。

How

Deep Compression 的实现主要有三步,如下图所示:
pipeline
包括Pruning(权值修剪),Quantization(权值共享和量化),Huffman Coding(Huffman编码)。

1.Prunning

如果你调试过caffe模型,观察里面的权值,会发现大部分权值都集中在-1~1之间,即非常小,另一方面,神经网络的提出就是模仿人脑中的神经元突触之间的信息传导,因此这数量庞大的权值中,存在着不可忽视的冗余性,这就为权值修剪提供了根据。pruning可以分为三步:
step1. 正常训练模型得到网络权值;
step2. 将所有低于一定阈值的权值设为0;
step3. 重新训练网络中剩下的非零权值。
经过权值修剪后的稀疏网络,就可以用一种紧凑的存储方式CSC或CSR(compressed sparse column or compressed sparse row)来表示。这里举个栗子来解释下什么是CSR
假设有一个原始稀疏矩阵A
A
CSR可以将原始矩阵表达为三部分,即AA,JA,IC
A
其中,AA是矩阵A中所有非零元素,长度为a,即非零元素个数;
JA是矩阵A中每行第一个非零元素在AA中的位置,最后一个元素是非零元素数加1,长度为n+1, n是矩阵A的行数;
IC是AA中每个元素对应的列号,长度为a。
所以将一个稀疏矩阵转为CSR表示,需要的空间为2*a+n+1个,同理CSC也是类似。
可以看出,为了达到压缩原始模型的目的,不仅需要在保持模型精度的同时,prune掉尽可能多的权值,也需要减少存储元素位置index所带来的额外存储开销,故论文中采用了存储index difference而非绝对index来进一步压缩模型,如下图所示:
index
其中,第一个非零元素的存储的是他的绝对位置,后面的元素依次存储的是与前一个非零元素的索引差值。在论文中,采用固定bit来存储这一差值,以图中表述为例,如果采用3bit,则最大能表述的差值为8,当一个非零元素距其前一个非零元素位置超过8,则将该元素值置零。(这一点其实也很好理解,如果两个非零元素位置差很多,也即中间有很多零元素,那么将这一元素置零,对最终的结果影响也不会很大)
做完权值修剪这一步后,AlexNet和VGG-16模型分别压缩了9倍和13倍,表明模型中存在着较大的冗余。

2.Weight Shared & Quantization

为了进一步压缩网络,考虑让若干个权值共享同一个权值,这一需要存储的数据量也大大减少。在论文中,采用kmeans算法来将权值进行聚类,在每一个类中,所有的权值共享该类的聚类质心,因此最终存储的结果就是一个码书和索引表。
1.对权值聚类
论文中采用kmeans聚类算法,通过优化所有类内元素到聚类中心的差距(within-cluster sum of squares )来确定最终的聚类结果:
WCSS
式中,W ={w1,w2,…wn}是n个原始权值,C={c1,c2,…ck}是k个聚类。
需要注意的是聚类是在网络训练完毕后做的,因此聚类结果能够最大程度地接近原始网络权值分布。
2. 聚类中心初始化
常用的初始化方式包括3种:
a) 随机初始化。即从原始数据种随机产生k个观察值作为聚类中心。
b) 密度分布初始化。现将累计概率密度CDF的y值分布线性划分,然后根据每个划分点的y值找到与CDF曲线的交点,再找到该交点对应的x轴坐标,将其作为初始聚类中心。
c) 线性初始化。将原始数据的最小值到最大值之间的线性划分作为初始聚类中心。
三种初始化方式的示意图如下所示:
initialization
由于大权值比小权值更重要(参加HanSong15年论文),而线性初始化方式则能更好地保留大权值中心,因此文中采用这一方式,后面的实验结果也验证了这个结论。
3. 前向反馈和后项传播
前向时需要将每个权值用其对应的聚类中心代替,后向计算每个类内的权值梯度,然后将其梯度和反传,用来更新聚类中心,如图:
这里写图片描述
共享权值后,就可以用一个码书和对应的index来表征。假设原始权值用32bit浮点型表示,量化区间为256,即8bit,共有n个权值,量化后需要存储n个8bit索引和256个聚类中心值,则可以计算出压缩率compression ratio:
r = 32*n / (8*n + 256*32 )≈4
可以看出,如果采用8bit编码,则至少能达到4倍压缩率。

3.Huffman Coding

Huffman 编码是最后一步,主要用于解决编码长短不一带来的冗余问题。因为在论文中,作者针对卷积层统一采用8bit编码,而全连接层采用5bit,所以采用这种熵编码能够更好地使编码bit均衡,减少冗余。

4.Evaluation

实验结果就是能在保持精度不变(甚至提高)的前提下,将模型压缩到前所未有的小。直接上图有用数据说话。
这里写图片描述
这里写图片描述

5.Discussion

这里写图片描述
不同模型压缩比和精度的对比,验证了pruning和quantization一块做效果最好。

这里写图片描述
不同压缩bit对精度的影响,同时表明conv层比fc层更敏感, 因此需要更多的bit表示。

这里写图片描述
不同初始化方式对精度的影响,线性初始化效果最好。

这里写图片描述
卷积层采用8bit,全连接层采用5bit效果最好。

What

此部分讲一讲修改caffe源码的过程。其实只要读懂了文章原理,修改起来很容易。
对pruning过程来说,可以定义一个mask来“屏蔽”修剪掉的权值,对于quantization过程来说,需定义一个indice来存储索引号,以及一个centroid结构来存放聚类中心。
在include/caffe/layer.hpp中为Layer类添加以下成员变量:
这里写图片描述
以及成员函数:
这里写图片描述
由于只对卷积层和全连接层做压缩,因此,只需修改这两个层的对应函数即可。

在include/caffe/layers/base_conv_layer.hpp添加成员函数
这里写图片描述

这两处定义的函数都是基类的虚函数,不需要具体实现。

在include/caffe/layers/conv_layer.hpp中添加成员函数声明:
这里写图片描述
类似的,在include/caffe/layers/inner_product_layer.hpp也添加该函数声明。
在src/caffe/layers/conv_layer.cpp 添加该函数的声明,用于初始化mask和对权值进行聚类。
这里写图片描述

同时,修改前向和后向函数。
在前向函数中,需要将权值用其聚类中心表示,红框部分为添加部分:
这里写图片描述
在后向函数中,需要添加两部分,一是对mask为0,即屏蔽掉的权值不再进行更新,即将其weight_diff设为0,另一个则是统计每一类内的梯度差值均值,并将其反传回去,红框内为添加部分。
这里写图片描述
这里写图片描述
kmeans的实现如下,当然也可以用Opencv自带的,速度会更快些。

template<typename Dtype>
void kmeans_cluster(vector<int> &cLabel, vector<Dtype> &cCentro, Dtype *cWeights, int nWeights, vector<int> &mask, /*Dtype maxWeight, Dtype minWeight,*/  int nCluster,  int max_iter /* = 1000 */)
{
    //find min max
    Dtype maxWeight=numeric_limits<Dtype>::min(), minWeight=numeric_limits<Dtype>::max();
    for(int k = 0; k < nWeights; ++k)
    {
        if(mask[k])
        {
            if(cWeights[k] > maxWeight)
                maxWeight = cWeights[k];
            if(cWeights[k] < minWeight)
                minWeight = cWeights[k];
        }
    }
    // generate initial centroids linearly
    for (int k = 0; k < nCluster; k++)
        cCentro[k] = minWeight + (maxWeight - minWeight)*k / (nCluster - 1);

    //initialize all label to -1
    for (int k = 0; k < nWeights; ++k)
        cLabel[k] = -1;

    const Dtype float_max = numeric_limits<Dtype>::max();
    // initialize
    Dtype *cDistance = new Dtype[nWeights];
    int *cClusterSize = new int[nCluster];

    Dtype *pCentroPos = new Dtype[nCluster];
    int *pClusterSize = new int[nCluster];
    memset(pClusterSize, 0, sizeof(int)*nCluster);
    memset(pCentroPos, 0, sizeof(Dtype)*nCluster);
    Dtype *ptrC = new Dtype[nCluster];
    int *ptrS = new int[nCluster];

    int iter = 0;
    //Dtype tk1 = 0.f, tk2 = 0.f, tk3 = 0.f;
    double mCurDistance = 0.0;
    double mPreDistance = numeric_limits<double>::max();

    // clustering
    while (iter < max_iter)
    {
        // check convergence
        if (fabs(mPreDistance - mCurDistance) / mPreDistance < 0.01) break;
        mPreDistance = mCurDistance;
        mCurDistance = 0.0;

        // select nearest cluster

        for (int n = 0; n < nWeights; n++)
        {
            if (!mask[n])
                continue;
            Dtype distance;
            Dtype mindistance = float_max;
            int clostCluster = -1;
            for (int k = 0; k < nCluster; k++)
            {
                distance = fabs(cWeights[n] - cCentro[k]);
                if (distance < mindistance)
                {
                    mindistance = distance;
                    clostCluster = k;
                }
            }
            cDistance[n] = mindistance;
            cLabel[n] = clostCluster;
        }


        // calc new distance/inertia

        for (int n = 0; n < nWeights; n++)
        {
            if (mask[n])
                mCurDistance = mCurDistance + cDistance[n];
        }


    // generate new centroids
    // accumulation(private)

        for (int k = 0; k < nCluster; k++)
        {
            ptrC[k] = 0.f;
            ptrS[k] = 0;
        }

        for (int n = 0; n < nWeights; n++)
        {
            if (mask[n])
            {
                ptrC[cLabel[n]] += cWeights[n];
                ptrS[cLabel[n]] += 1;
            }
        }

        for (int k = 0; k < nCluster; k++)
        {
            pCentroPos[ k] = ptrC[k];
            pClusterSize[k] = ptrS[k];
        }

        //reduction(global)
        for (int k = 0; k < nCluster; k++)
        {

            cCentro[k] = pCentroPos[k];
            cClusterSize[k] = pClusterSize[k];

            cCentro[k] /= cClusterSize[k];
        }

        iter++;
    //  cout << "Iteration: " << iter << " Distance: " << mCurDistance << endl;
        }
        //gather centroids
        //#pragma omp parallel for
        //for(int n=0; n<nNode; n++)
        //    cNodes[n] = cCentro[cLabel[n]];

        delete[] cDistance;
        delete[] cClusterSize;
        delete[] pClusterSize;
        delete[] pCentroPos;
        delete[] ptrC;
        delete[] ptrS;
}

全连接层的修改和卷积层的一致不再赘述。同样的,可以把对应的.cu文件中的gpu前向和后向函数实现也修改了,方便gpu训练。
最后,在src/caffe/net.cpp的CopyTrainedLayersFrom(const NetParameter& param)函数中调用我们定义的函数,即在读入已经训练好的模型权值时,对每一层做需要的权值mask初始化和权值聚类。
这里写图片描述
至此代码修改完毕,编译运行即可。

Reference

[1] SqueezeNet: AlexNet-level accuracy with 50x fewer parameters and <0.5MB model size
[2] Deep Compression: Compressing deep neural networks with pruning, trained quantization and Huffman coding
[3] Learning both Weights and Connections for Efficient Neural Networks
[4] Efficient Inference Engine on Compressed Deep Neural Network

总结:

最后再提一句,几乎所有的模型压缩文章都是从Alexnet和VGG下手,一是因为他们都采用了多层较大的全连接层,而全连接层的权值甚至占到了总参数的90%以上,所以即便只对全连接层进行“开刀”,压缩效果也是显著的。另一方面,这些论文提出的结果在现在看来并不是state of art的,存在可提升的空间,而且在NIN的文章中表明,全连接层容易引起过拟合,去掉全连接层反而有助于精度提升,所以这么看来压缩模型其实是个不吃力又讨好的活,获得的好处显然是双倍的。但运用到特定的网络中,还需要不断反复试验,因地制宜,寻找适合该网络的压缩方式。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Deep Compression阅读理解及Caffe源码修改 的相关文章

  • 使用 IIS-Express 激活压缩(尤其是动态压缩)

    是否可以在 IIS Express 上启用动态压缩 针对 WCF 服务 这是一个开发环境问题 因此我无法使用完整版本 但我需要弄清楚它在压缩时的表现如何 进入 IIS Express 安装文件夹 programfiles IIS Expre
  • DICOM 文件压缩

    我的工作需要使用 DICOM 文件 每个 DICOM 文件由单个目录中的许多 dcm 文件组成 我需要通过网络发送这些文件 由于文件很大 这个过程在某种程度上是这样的 我也是一名程序员 我想知道压缩此类文件的理想方法是什么 我说的是在本地计
  • 如何使用 PHP 创建 .gz 文件?

    我想使用 PHP 在我的服务器上 gzip 压缩文件 有人有一个输入文件并输出压缩文件的例子吗 这段代码可以解决问题 Name of the file we re compressing file test txt Name of the
  • 为什么 tarfile 模块不允许压缩附加?

    没有直接的方法可以追加到压缩的 tar 存档中 作为文档状态 https docs python org 3 library tarfile html tarfile open 注意 a gz a bz2 or a xz 不可能 作为解决方
  • MySQL中如何压缩列?

    我有一个存储电子邮件通信的表 每当有人 回复 整个路径也被包含并保存到 数据库 我需要这样 因为应用程序的数量 级别更改以纠正太高的情况 尺寸mail文本列是10000 但是 我在存储文本时遇到的困难还不止这些 由于我不确定可以发生多少个通
  • Tomcat8 Gzip 压缩 CSS、JS

    我正在使用 tomcat8 并尝试模拟 CSS 和 JS 的 GZIP 压缩 我已在 server xml 中添加了条目 如下所示
  • 上传前压缩相机图像

    我正在使用这段代码 来自www internetria com http www internetria com blog 2013 04 12 android enviar imagenes por webservice 拍照并上传到服务
  • Caffe:如果内存中只能容纳一小部分,我该怎么办?

    我正在尝试训练一个非常大的模型 因此 我只能将非常小的批量大小放入 GPU 内存中 处理小批量的结果非常噪声梯度估计 https stackoverflow com a 33717093 1714410 我该怎么做才能避免这个问题 您可以更
  • 不同 zlib 压缩级别的压缩比

    我正在考虑使用什么级别的 zlib 压缩 并且我很好奇可以在 zlib 命令中指定的不同压缩级别的不同压缩率 这zlib手册 http www zlib net manual html Constants具有以下用于指定压缩级别的常量 de
  • 如何使用 .NET 压缩目录?

    我有一个包含多个文件的目录 我想将此文件夹压缩为 zip 或 tar gz 文件 我怎样才能用 C 完成他的工作 您可以使用DotNetZip 库 http www codeplex com DotNetZip 它有相当丰富和有用的功能 E
  • Java 中客户端/服务器传输的压缩字符串

    我使用专有的客户端 服务器消息格式来限制我可以通过网络发送的内容 我无法发送序列化对象 我必须将消息中的数据存储为字符串 我发送的数据是大的逗号分隔值 我想在将数据作为字符串打包到消息中之前对其进行压缩 我尝试使用 Deflater Inf
  • 从选定位置导入 Python 模块

    假设我有三个脚本 Main py 包含所有导入 1 py 随机脚本 2 py 随机脚本 pyinstaller F onedir Main py 80mb pyinstaller F onedir 1 py 80mb pyinstaller
  • 如何在 Caffe 的网络中出现多次损失?

    如果我在网络中定义多个损失层 从这些末端到网络的开头是否会发生多个反向传播 我的意思是 他们真的是这样工作的吗 假设我有这样的事情 Layer1 Layer2 Layer n Layer cls1 bottom layer n top cl
  • 使用 Android 创建 ZIP 文件

    如何从 XML 文件创建 ZIP 文件 我想以 XML 格式备份所有收件箱消息 并压缩 XML 文件并将其存储在SD card http en wikipedia org wiki Secure Digital 下面的代码解决了我的问题 p
  • C++ 压缩字节数组

    大家好 我加载一组图像并生成体积数据 我将此体积数据保存在 无符号字符 体积 array 现在我想将此数组保存在文件中并检索 但在保存之前我想压缩字节数组 因为卷数据很大 这方面有什么建议吗 提前致谢 volume在你的例子中不是一个数组
  • 快速搜索压缩文本文件

    我需要能够在大量压缩文件 txt 中搜索文本 压缩可能会改变为其他东西 甚至成为专有的 我想避免解压所有文件并压缩 编码 搜索字符串并在压缩文件中搜索 这应该可以通过对所有文件使用相同的码本使用霍夫曼压缩来实现 我不想重新发明轮子 所以 任
  • iOS 视频压缩 Swift iOS 8 损坏的视频文件

    我正在尝试压缩用户相机从 UIImagePickerController 拍摄的视频 不是现有视频 而是动态视频 以上传到我的服务器 并花费少量时间来完成此操作 因此较小的尺寸是理想的选择 而不是 30 较新质量的相机为 45 mb 这是在
  • Caffe 的 LSTM 模块

    有谁知道 Caffe 是否有一个不错的 LSTM 模块 我从 russel91 的 github 帐户中找到了一个 但显然包含示例和解释的网页消失了 以前是http apollo deepmatter io http apollo deep
  • 无需构建树即可预测霍夫曼压缩比

    我有一个二进制文件 我知道其中每个符号出现的次数 如果我要使用霍夫曼算法压缩它 我需要预测压缩文件的长度 我只对假设的输出长度感兴趣 而不对单个符号的代码感兴趣 因此构建霍夫曼树似乎是多余的 作为一个例子 我需要得到类似的东西 包含 4 个
  • 如何在.NET中使用java.util.zip.Deflater解压缩放气流?

    之后我有一个转储java util zip Deflater 可以确认它是有效的 因为 Java 的Inflater打开它很好 并且需要在 NET中打开它 byte content ReadSample sampleName var inp

随机推荐

  • 【macbook air M1】采用Rosetta方式运行,在macbook M1 Air上比macBook Pro M1,速度慢一些的原因?

    My application use rosetta run on macbook air M1 slowly than run on macbook M1 Why xff1f It 39 s possible that you 39 re
  • python serial模块

    安装注意 xff1a 1 easy install 是无法安装 提示找不到serial 2 可以通过exe文件安装 xff0c 不过网上的exe多数是针对win32操作系统的 3 最保险的安装方式 https pypi python org
  • libcurl长连接高并发高性能

    libcurl长连接高并发高性能 自己开发了一个股票智能分析软件 xff0c 功能很强大 xff0c 需要的点击下面的链接获取 xff1a https www cnblogs com bclshuai p 11380657 html 目录
  • 树莓派3B使用mavlink串口连接PIXHAWK_V5

    参考网址 xff1a http ardupilot org dev docs raspberry pi via mavlink html https dev px4 io en robotics dronekit html https do
  • C++ 通过TCP Socket实现简单Http服务器

    实现一个简单的Http服务器 xff0c 基于windows 平台 总共五个文件 HttpServer hpp HttpServer cpp Utils hpp Utils cpp main cpp Utils hpp span class
  • Java并发编程--自旋锁的实现和使用

    1 自旋锁介绍 自旋锁是这样一类锁 xff1a 当线程等待加锁时 xff0c 不会阻塞 xff0c 不会进入等待状态 xff0c 而是保持运行状态 大致的思路是 xff1a 让当前线程不停地的在循环体内执行 xff0c 当循环的条件被其他线
  • [Ubuntu] 可用云盘-尝试记录

    Ubuntu可用云盘 xff1a https github com Aruelius cloud189 说明 xff1a 这是一个调用天翼云API的开源命令行工具 xff0c 基于Python编写 xff1b 天翼云官方没有客户端支持Lin
  • 【无人机】基于遗传算法实现无人机编队位置规划附matlab代码

    1 内容介绍 现代社会的无人机成本造价低 不易损耗 轻巧灵便 易躲藏 能精确打击 目标这些特点 xff0c 使其在一些高危任务中发挥了不可替代的作用 5 无人机的用处主要有两种 xff1a 民用和军事 在民用方面 xff0c 我们可以运用无
  • 【路径规划】基于DWA实现机器人动态避障附matlab代码

    1 内容介绍 DWA 算法是基于机器人运动学与动力学理论的一种局部避障算法 xff0c 它将对机器人的位置控制转换为对机器人的速度控制 DWA 算法可以概括为三步 一是根据机器人自身的限制以及环境制约将速度的采样空间约束在一定范围内 二是根
  • 飞控pixhawk硬件框架

    本文转载于 xff1a https blog csdn net csshuke article details 78952026 xfeff xfeff 1 Phxhawk连接线路 2 Phxhawk硬件芯片列表 处理器 STM32F427
  • PCB_layout_misc

    AD的规则设置参考 https blog csdn net geek monkey article details 80249217 一些PCB厂家的工艺 嘉立创https www jlc com portal vtechnology ht
  • 怎样把经纬度坐标转换为空间直角坐标

    怎样把经纬度坐标转换为空间直角坐标 假设你的空间直角坐标以地球球心为原点 原点到北极为正z轴 原点到经纬度 0 0 为正x轴 那么纬度a 北正南负 经度b 东正西负 的空间直角坐标为 x 61 Rcos a cos b y 61 Rcos
  • APM添加参数

    APM添加参数 参考 https ardupilot org dev docs code overview adding a new parameter html 添加应用参数模块 例如 battery Parameters h k par
  • pixhawk6x/5x 电源插座/插头的型号

    型号 xff1a molex CLIK Mate 5024430670 5024390600
  • intellij idea: git tag操作 及 master branch相互合并操作

    git tag和git branches区别 xff1f tag就像是里程碑标志的一个点 branch是一个新的征程的一条线 tag是静态的 是只读的 不能修改 而branch是要往前走的 稳定版本备份用tag 新功能开发多人用branch
  • C++之STL和Boost

    最近一年我电话面试了数十位 C 43 43 应聘者 xff0c 惯用的暖场问题是 工作中使用过 STL 的哪些组件 xff1f 使用过 Boost 的哪些组件 xff1f 得到的答案大多集中在 vector map 和 shared ptr
  • ubuntu 下安装intel realsense驱动

    在安装之前一定要确保系统是ubuntu 14 04 3 64位 xff01 在安装之前一定要确保系统是ubuntu 14 04 3 64位 xff01 在安装之前一定要确保系统是ubuntu 14 04 3 64位 xff01 重要的事情说
  • windows下安装numpy,scipy遇到的问题总结

    最近开始研究3D手势识别 xff0c 下载的源码包是基于python的 xff0c 需要用到扩展包numpy scipy等 xff0c 安装过程汇总遇到的问题总结如下 xff1a 1 安装numpy 下载numpy编译包 xff0c 进入该
  • Linux大数据处理踩坑实录

    最近开发需要在linux服务器上做大数据处理 xff0c 由于对Linux开发并不是很熟悉 xff0c 因此踩了很多坑 xff0c 先作如下记录 xff1a 1 bash shell实现多进程 背景如下 需要从hadoop的hdfs上向服务
  • Deep Compression阅读理解及Caffe源码修改

    更新 xff1a 没想到这篇文章写出后有这么多人关注和索要源码 xff0c 有点受宠若惊 说来惭愧 xff0c 这个工作当时做的很粗糙 xff0c 源码修改的比较乱 xff0c 所以一直不太好拿出手 最近终于有时间整理了一下代码并开源出来了