【GitHub代码阅读】Deep-Compression-AlexNet-master的代码注释

本文详细解释了如何使用DeepCompression技术压缩神经网络(如AlexNet),涉及代码注释,包括从二进制文件到Caffe模型的转换过程,以及与硬件加速器的集成。重点介绍了权重处理和模型优化的方法,以及Caffe框架的运用。
摘要由CSDN通过智能技术生成

Deep-Compression-AlexNet-master的代码注释

汤匙的代码能力较弱,如果有理解不到位或者错误的地方,各位批评指正

'''
If you find Deep Compression useful in your research, please consider citing the paper:


@inproceedings{han2015learning,  
  title={Learning both Weights and Connections for Efficient Neural Network},
  author={Han, Song and Pool, Jeff and Tran, John and Dally, William},
  booktitle={Advances in Neural Information Processing Systems (NIPS)},
  pages={1135--1143},  
  year={2015}  
}


@article{han2015deep_compression, 
  title={Deep Compression: Compressing Deep Neural Networks with Pruning, Trained Quantization and Huffman Coding},
  author={Han, Song and Mao, Huizi and Dally, William J},
  journal={International Conference on Learning Representations (ICLR)},
  year={2016}
}

A hardware accelerator working directly on the deep compressed model:

@article{han2016eie,
  title={EIE: Efficient Inference Engine on Compressed Deep Neural Network},
  author={Han, Song and Liu, Xingyu and Mao, Huizi and Pu, Jing and Pedram, Ardavan and Horowitz, Mark A and Dally, William J},
  journal={International Conference on Computer Architecture (ISCA)},
  year={2016}
}
'''

'''
这段代码的功能是将一个二进制文件转换为一个caffe模型文件,caffe是一个用于深度学习的开源框架。

首先,从命令行参数中获取输入的二进制文件和输出的caffe模型文件的路径,以及网络的prototxt文件的路径,prototxt文件是一个文本文件,用于定义网络的结构和参数。
然后,检查系统变量CAFFE_ROOT是否已经设置,这个变量指定了caffe的根目录,如果没有设置,就提示用户设置并退出程序。
接着,导入caffe模块,并创建一个caffe.Net对象,用于加载prototxt文件和保存caffe模型文件。
然后,从二进制文件中读取每一层的非零元素的个数,以及每一层的编码字典、偏置、稀疏编码流和索引流,这些数据都是用numpy的fromfile函数读取的,具体的数据类型和个数由文件的格式决定。
最后,对于每一层,调用binary_to_net函数,将二进制数据转换为网络的权重矩阵,并复制到caffe.Net对象的params属性中,同时也复制偏置数据。binary_to_net函数的主要逻辑是:
    根据编码字典的大小,确定每个像素的位数和每个字节可以存储的像素个数,以及创建一个全零的编码数组。
    从稀疏编码流和索引流中恢复稀疏编码数组和索引数组,这里用到了一些位运算和累加运算。
    根据索引数组和稀疏编码数组,将编码数组的相应位置赋值为稀疏编码,然后根据编码字典,将编码数组转换为权重矩阵,最后复制到网络的权重数据中。
代码的最后一行是调用caffe.Net对象的save方法,将网络的参数保存为caffe模型文件。然后打印一句提示信息,告诉用户转换完成,并建议用户测试模型的准确性。
'''

import sys
import os
import numpy as np
import pickle

help_ = '''
Usage:
    decode.py <net.prototxt> <net.binary> <target.caffemodel>
    Set variable CAFFE_ROOT as root of caffe before run this demo!
'''

'''一个命令行程序,需要接收三个参数。如果没有提供三个参数,它会打印出帮助信息,并退出程序。
如果提供了三个参数,它会将它们分别赋值给prototxt, net_bin, target这三个变量。
这三个变量是用来加载一个神经网络模型的。
prototxt是一个文本文件,它定义了神经网络的结构和参数1。 
net_bin是一个二进制文件,它存储了神经网络的权重2。 
target是一个目标文件,它可能是用来保存或测试神经网络的输出的'''

if len(sys.argv) != 4:
    print(help_)
    sys.exit()
else:
    prototxt = sys.argv[1]  # 定义了神经网络的结构和参数
    net_bin = sys.argv[2]   # 存储了神经网络的权重
    target = sys.argv[3]    # 保存或测试神经网络的输出

# os.system("cd $CAFFE_ROOT")
try:
    caffe_root = os.environ["CAFFE_ROOT"]
except KeyError:
    print("Set system variable CAFFE_ROOT before running the demo!")
    sys.exit()

sys.path.insert(0, caffe_root + '/python')

import caffe

caffe.set_mode_cpu()  # 使用CPU模式
net = caffe.Net(prototxt, caffe.TEST)  # 创建一个Net对象,根据prototxt文件定义的网络结构,使用TEST模式
# 从Net对象中筛选出包含’conv’,'fc’或’ip’的层的名称,存储在layers变量中。filter函数用来过滤掉不符合条件的元素
layers = filter(lambda x: 'conv' in x or 'fc' in x or 'ip' in x, net.params.keys())

fin = open(net_bin, 'rb')  # 获取神经网络权重


# 二进制转网络(权重、spm流、ind流、代码书、数?)
# spm_stream和ind_stream是两个二进制流,分别包含了spm和ind的数据,由np.fromfile函数从文件中读取。
# codebook是codebook_size=2 ** bits(256 或 16)个从weight中取出的数据(全局变量bits=8或4,与该函数中的bits无关,不要混淆
# num_nz是网络中网络层的数量
# 用np.fromfile函数从一个打开的文件对象fin中读取一定数量的无符号8位整数,作为一个NumPy数组赋值给ind_stream和ind_stream变量
# # spm_stream = np.fromfile(fin, dtype=np.uint8, count=(nz_num[idx] - 1) / (8 / bits) + 1)  # 读取文件(bits=8或4)
# # ind_stream = np.fromfile(fin, dtype=np.uint8, count=(nz_num[idx] - 1) / 2 + 1)
def binary_to_net(weights, spm_stream, ind_stream, codebook, num_nz):
    bits = np.log2(codebook.size)  # 根据bits=log(256或16)=8或4的值,设置slots的值,并创建一个全零数组code
    if bits == 4:  # 16  --bits是一个变量,表示每个像素的位数,也就是颜色的深度。bits的值可以是4或8,分别对应16种或256种颜色。
        slots = 2  # 如果bits是4,那么每个字节可以存储2个像素,所以slots是2
    elif bits == 8:  # 256  --slots是一个变量,表示每个字节可以存储的像素个数。
        slots = 1  # 如果bits是8,那么每个字节可以存储1个像素,所以slots是1
    else:
        print("Not impemented,", bits)
        sys.exit()
    # code是一个数组,用于存储像素的编码。code的形状和类型与weights数组相同,都是无符号8位整数。code的初始值都是0,表示空白。
    code = np.zeros(weights.size, np.uint8)

    # Recover from binary stream  从二进制流恢复
    # 表示数组的长度或形状,也就是数组中有多少个元素。
    # np.uint8:表示数据类型为无符号8位整数,也就是每个元素都是0到255之间的整数
    spm = np.zeros(num_nz, np.uint8)  # spm(数组):存储稀疏数组的编码,形状和类型与weights数组相同,是无符号8位整数。
    ind = np.zeros(num_nz, np.uint8)  # 用于存储稀疏数组的索引,其形状和类型也与weights数组相同,都是无符号8位整数。

    if slots == 2:
        # 用numpy的arange函数生成两个等差数列,一个是从0开始,每隔2取一个数,直到num_nz-1;
        # 另一个是从1开始,每隔2取一个数,直到num_nz-1。
        # 这两个数列分别对应spm数组的偶数索引和奇数索引。
        spm[np.arange(0, num_nz, 2)] = spm_stream % (2**4)  # spm数组的偶数索引中存 余数
        spm[np.arange(1, num_nz, 2)] = spm_stream / (2**4)  # spm数组的奇数索引中存 商
    else:  # slots=1的时候
        spm = spm_stream

    ind[np.arange(0, num_nz, 2)] = ind_stream % (2**4)
    ind[np.arange(1, num_nz, 2)] = ind_stream / (2**4)
    # np.arange是一个函数,用于生成等差数列。%是一个运算符,用于求余数;/是一个运算符,用于求商数;**是一个运算符,用于求幂。
    '''
    用numpy的cumsum函数,对索引数组ind进行累加运算,得到一个新的数组,其中每个元素是原数组从开始位置到当前位置的所有元素的和,然后减去1,得到最终的索引数组。
    用最终的索引数组ind,从编码数组code中提取出相应的元素,赋值给稀疏编码数组spm,这样就完成了稀疏编码的解码过程。
    用numpy的reshape函数,根据权重数组weights的形状,将编码字典codebook中的相应元素重新排列成一个新的数组data,这样就完成了编码字典的解码过程。
    用numpy的copyto函数,将新的数组data的值复制到权重数组weights中,这样就完成了二进制数据到网络权重的转换过程。
    #'''
    # Recover the matrix 恢复到矩阵
    # np.cumsum是一个用来计算数组的累积和的函数,可以通过指定axis参数来选择计算哪个轴向的累积和。
    # 接收一个数组作为输入,然后返回一个新的数组,其中每个元素是输入数组中从第一个元素到当前位置的所有元素的和
    ind = np.cumsum(ind+1)-1
    code[ind] = spm
    data = np.reshape(codebook[code], weights.shape)
    np.copyto(weights, data)


#############   以下是主程序  ###############
nz_num = np.fromfile(fin, dtype=np.uint32, count=len(layers))  # 获取网络层
# count=len(layers)表示读取的数量为len(layers),也就是一个变量layers的长度,它可能是一个列表或者一个数组
# 用for循环和enumerate函数对layers变量进行遍历,每次循环时,idx变量表示当前元素的索引,layer变量表示当前元素的值3。
# layers变量可能是一个列表或者一个数组,它的内容可能是一些神经网络层的名称。
for idx, layer in enumerate(layers):
    # print "Reconstruct layer 重建层", layer
    # print "Total Non-zero number 总共的非零数:", nz_num[idx]
    if 'conv' in layer:  # 在卷积层时,是 8 Bit代表一个像素值
        bits = 8
    else:
        bits = 4  # 在全连接fc或内积层ip是4bit代表一个像素值
        '''
        神经网络中的ip层是内积层(inner product layer)的简称1。
        它是一种全连接层(fully connected layer),也就是每个输入节点都与每个输出节点相连,通过矩阵乘法和偏置加法来计算输出2。
        它的作用是将高维的输入数据映射到低维的输出空间,通常用于神经网络的最后一层,进行分类或回归
        '''
    codebook_size = 2 ** bits  # 编码大小的2的bits次方
    # (文件、类型、读取的数量)
    codebook = np.fromfile(fin, dtype=np.float32, count=codebook_size)  # 读取文件:数据格式为floats32
    bias = np.fromfile(fin, dtype=np.float32, count=net.params[layer][1].data.size)  # 获取偏置的数量和具体数值
    # 它可以接收两个数组作为输入,然后将第一个数组的内容复制到第二个数组中,覆盖原有的数据。
    # 通过指定where参数来选择复制的位置,或者通过指定casting参数来选择复制的类型。
    np.copyto(net.params[layer][1].data, bias)  # 将bias数组的内容复制到net.params[layer][1].data中,覆盖原有的数据

    # 用np.fromfile函数从一个打开的文件对象fin中读取一定数量的无符号8位整数,作为一个NumPy数组赋值给ind_stream和ind_stream变量
    spm_stream = np.fromfile(fin, dtype=np.uint8, count=(nz_num[idx]-1) / (8/bits) + 1)  # 读取文件(bits=8或4)
    ind_stream = np.fromfile(fin, dtype=np.uint8, count=(nz_num[idx]-1) / 2 + 1)  #

    # (网络参数,spm流,ind流,码,
    binary_to_net(net.params[layer][0].data, spm_stream, ind_stream, codebook, nz_num[idx])

net.save(target)
print("All done! See your output caffemodel and test its accuracy.")

'''
net.params[layer][0].data和net.params[layer][1].data是Caffe框架中的一种表示神经网络层参数的方式。

它们分别表示层layer的权重矩阵(W)和偏置向量(b),它们是用NumPy数组类型存储的。它们的形状和数据类型取决于层layer的类型和设置。

例如,如果层layer是一个卷积层, 
那么net.params[layer][0].data的形状是(输出通道数, 输入通道数, 卷积核高度, 卷积核宽度),
net.params[layer][1].data的形状是(输出通道数,)。         
如果层layer是一个全连接层,那么net.params[layer][0].data的形状是(输出节点数, 输入节点数),
net.params[layer][1].data的形状是(输出节点数,)。 
'''

readme.md文件中有指导如何运行该项目,但是仍旧不会在win11环境下的pycharm中运行。后期要是能够运行了,续杯~~~

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值