1. sys.argv[1:]  # 在控制台进行参数的输入时,只使用第二个参数以后的数据

参数说明:控制台的输入:python test.py what, 使用sys.argv[1:],那么将获得what这个数值

# test.py

import sys
print(sys.argv[1:])

2. tf.split(value=x, num_or_size_split=2, axis=3) # 对数据进行切分操作,比如原始维度为[1, 227, 227, 96], 切分后的维度为[2, 1, 227, 227, 48]

参数说明:value表示输入数据,num_or_size_split切分的数据大小,axis对第几个维度进行切分

3.tf.concat(values=x, axis=3) # 表示对数据进行合并操作,比如存在[1, 64, 64, 128], [1, 64, 64, 128] 合并后的大小为[1, 64, 64, 256]

参数说明:values表示输入数据,axis表示合并的维度

4. tf.variable_scope(name, reuse=True) # 表示在name层名的范围内,对参数进行复用操作

参数说明:name表示层的名字,reuse=True表示进行复用操作

5.tf.get_variable('w', shape=[5, 5, channel/group, num_filter], trainable) # 创建参数,如果存在就进行参数的复用

代码说明:'w'表示参数名,shape表示参数的大小,trainable表示对参数是否进行冻结操作,即不参与参数的更新

6. np.load('a.npy', encoding='bytes').item()  # 进行.npy文件的读取,npy文件是一种Python的独有的读取文件

参数说明:‘a.npy’表示文件名,encoding表示编码方式,item()表示获得文件的内容

a = {'a':1, 'b':2}
np.save('a.npy', a)
c = np.load('a.npy', encoding='bytes').item()
print(c)

 

1深度学习原理与框架-Alexnet(迁移学习代码)  1.sys.argv[1:](控制台输入的参数获取第二个参数开始) 2.tf.split(对数据进行切分操作) 3.tf.concat(对数据进行合并操作) 4.tf.variable_scope(指定w的使用范围) 5.tf.get_variable(构造和获得参数) 6.np.load(加载.npy文件)

                              Alex网络结构图

代码说明:这里只进行图片分类的测试,并不进行图片的训练,使用的方法是读入.npy文件,获得的数据是字典类型的参数数据,字典的键是’conv1‘,'fc6'每一层的名字,字典的值是一个大列表,列表中有两个数据,一个是w的值,一个是b的值,对于w的值,其len(w.shape) > 1, 对于b的值,其len(b.shape) = 1 

需要做的是:构建Alexnet的网络结构,读入npy已经训练好的参数,并进行训练

数据说明:测试数据为3张图片,使用cv2.resize将数据的维度变成(227, 227)

对代码部分进行说明:这套代码的特别点:主要是在卷积层的特殊之处

在第二层,第五层和第六层卷积,分别进行了分开卷积再合并的操作,

如果是分开卷积再合并的话,那么卷积核的维度为[Kheight, Kwidth, chanel/groups, num_filter], 因为数据的第三个维度为channel/groups

同时需要将w和x进行split切分,即各自的第四个维度平分成两半,使用列表来存放结果,最后在使用tf.concat进行合并,再使用tf.nn.add_bias添加偏置项,使用激活函数进行激活操作

还有就是使用了with tf.variable_scope(name) as scope,  以及tf.get_variable('w', shape=[]), 在读取的参数过程中,使用参数名‘fc6‘作为参数的范围reuse=True,

tf.get_variable(获得上面定义的参数), 使用.assign将载入的参数赋予’w‘, 演示代码

a = {'a':1, 'b':2}
np.save('a.npy', a)
c = np.load('a.npy', encoding='bytes').item()
print(c)

with tf.variable_scope('a'):
    w = tf.get_variable('w', shape=[1], initializer=tf.constant_initializer(10))

with tf.variable_scope('b'):
    w = tf.get_variable('w', shape=[1], initializer=tf.constant_initializer(20))

with tf.Session() as sess:
    for name in c:
        x = np.reshape(c[name], w.shape)
        with tf.variable_scope(name, reuse=True):
            w = tf.get_variable('w', trainable=False).assign(x)
            print(sess.run(w))

代码:

第一步:使用argpase.ArgumentParser() 构建输入参数,parser.add_argument添加参数,这里使用的参数是图片的来源args.images, 图片的地址args.path 

第二步:读取图片

            判断图片的来源方式:

                如果是folder, 使用lamda f: ’{}/{}‘.format(args.path, f) 构建文件的路径, 使用os.listdir(args.path) 遍历文件里的文件名,使用cv2.imread(withpath(f)) 读取文件, 使用os.path.isfile(withpath(f)) 判断是否是文件,使用dict将读取的结果进行组合

               如果是url,即网络上的图片地址,构建读取图片的函数,使用urllib.request.urlopen()打开文件路径,使用bytearray(resp.read()) 将读取的图片转换为二进制格式,使用cv2.imdecode() 将图片转换为utf-8类型

第三步:如果读到图片,进行参数的设置,

             dropout: 用于进行tf.nn.dropout的keep_prob 

             skip: 用于在后续的参数加载中,用来去掉不需要进行加载的参数

             numClass: 分类的结果数

             x: tf.placeholder() 用于进行输入数据的初始化

             imgMean: 图片的均值,用于后续的图片去均值

第四步:模型的实例化操作

            第一步:构建类AlexNet, 将传入的参数进行self操作,参数有x, dropout, numClass, skip, pathmodel 

            第二步:使用self.buildModel, 调用函数进行model的构建

                           第一步:构建卷积网络, 输入的参数为x, kheight, kwidth, strideX, strideY, numfilter, name, padding, groups=1

                           第二步:构建池化层,输入的参数为x, kheight, kwidth, strideX, strideY, name, padding

                           第三步:构建局部响应归一化

                           第四步:第二层卷积层,groups=2,即进行分层卷积的操作, 池化层,局部响应归一化

                           第五步:第三层卷积层, group=1,不进行分层卷积操作,池化层,局部响应归一化

                           第六步:将归一化后的结果,使用tf.reshape进行维度的变换

                           第七步:进行第一次全连接操作,输入参数为x, inputS, outputS, relu_flag, name, 使用的也是with tf.variable_scope(name) as scope

                           第八步:进行dropout操作

                           第九步:全连接层,drought操作

                           第十层:全连接,使用self.fc8 表示输出结果score,以便进行外部的调用

第五步:使用model.fc8获得score得分值

第六步:使用tf.nn.softmax(score) 获得概率值

第七步:使用with tf.Session() as sess来获得sess

第八步:使用model.load_model(sess) 来加载参数,将获得参数赋予给之前定义的参数

              第一步:在类中构造load_model(),传入的参数是sess 

              第二步:使用np.load(path, encoding='bytes').item() 读取npy文件, 读入的数据是字典格式

              第三步:循环字典的keys,判断key不在skip里面

              第四步:with tf.variable_scope(name, reuse=True), 即在这个范围内获得参数w,name等于’fc6‘等

              第五步:循环k, p in dict[name], 因为字典的键里面有两组参数,一种是b,一种是w

              第六步:根据维度的大小来判断是b,还是w,如果是w, 使用sess.run(tf.get_variable('w'., trainable=False) .assign(p))将w的数据变成p即字典的键,同理将b的值进行赋值操作

第九步:循环读入的图片字典,使用sess.run(softmax, feedict)获得实际的得分scores

             第一步:对每一张图片进行cv2.resize操作,同时减去ImageMean即均值

             第二步:使用np.argmax(sess.run(softmax, feed_dict={x:[resized]})) 获得最大位置的索引值

             第三步:caffe_classes.class_names[mmax] 获得最大索引值对应的类别名

第十步:进行作图操作

              第一步:定义文字的类型,cv2.FONT_HERSHEY_SIMPLEX

              第二步:使用cv2.putText() 进行文字的添加

              第三步:使用cv2.imshow() 进行画图,使用cv2.waitkey(0)

代码:testalex.py

import argparse
import cv2
import numpy as np
import tensorflow as tf
import sys
import os
import alexnet
import caffe_classes
import urllib.request


# 第一步:使用argparse构建输入参数
parser = argparse.ArgumentParser(description='classify some picture')
# 是够是本地路径
parser.add_argument('images', choices=['folder', 'url'], default='folder')
# 添加路径
parser.add_argument('path', help='the image path')
# 用于获得除了testAlex.py的其他参数
args = parser.parse_args(sys.argv[1:])

# 第二步:根据路径读图片数据
# 本地路径
if args.images == 'folder':
    # 使用lambda做路径的组合函数
    withPath = lambda f:'{}/{}'.format(args.path, f)
    # 使用os.listdir()遍历文件路径,获得图片名,判断组合路径下的文件是够是文件,如果是,就使用cv2.imread读取组合路径下的图片
    testImg = dict((f, cv2.imread(withPath(f))) for f in os.listdir(args.path) if os.path.isfile(withPath(f)))
# 网上图片的路径
elif args.images == 'url':
    # 根据url定义读取的文件
    def url2img(url):
        """url to image"""
        # 打开url
        resp = urllib.request.urlopen(url)
        # 将读取的文件转换为二进制类型
        image = np.asarray(bytearray(resp.read()))
        # 对图片进行解码操作,转换为utf-8
        image = cv2.imdecode(image, cv2.IMREAD_COLOR)
        return image
    # 将读取的图片转换为字典的格式
    testImg = {args.path : url2img(args.path)}

# 第三步:如果存在testImg,进行参数设置
if testImg.values():
    # 用于dropout参数层
    dropout = 1
    # 用于判断哪些参数不需要进行加载
    skip = []
    # 分类的类别数
    numClass = 1000
    # 使用tf.placeholder进行x的输入参数初始化
    x = tf.placeholder(tf.float32, [1, 227, 227, 3])
    # 图片的imgMean,原始图片训练的时候也是使用这个值的
    imgMean = np.array([104, 117, 124], dtype=np.float32)
    # 第四步:对模型进行实例化操作
    model = alexnet.AlexNet(x, dropout, numClass, skip)
    # 第五步:使用model.fc8获得模型的score得分
    score = model.fc8
    # 第六步:使用tf.nn.softmax获得模型的概率值
    prob = tf.nn.softmax(score)
    # 第七步:使用with tf.Session() as sess构造执行函数
    with tf.Session() as sess:
        # 第八步:使用model中的load_model函数加载模型的参数,并将参数赋值给定义好的参数
        model.load_model(sess)
        # 第九步:对读入的图片进行预测类别的操作
        for image in testImg.values():
            # 进行图像的维度变换,变为227,227,同时去除均值
            resized = cv2.resize(image.astype(np.float32), (227, 227)) - imgMean
            # sess.run获得prob值,再使用np.argmax获得最大值的索引值
            mmax = np.argmax(sess.run(prob, feed_dict={x:[resized]}))
            # 获得索引值对应的标签名
            res = caffe_classes.class_names[mmax]
            # 第十步:进行作图操作
            # 字体
            font = cv2.FONT_HERSHEY_SIMPLEX
            # 将文字添加到图片中
            cv2.putText(image, res, (int(image.shape[0]/3), int(image.shape[1]/3)), font, 1, (0, 0, 255), 3)
            # 图片的展示
            cv2.imshow('image', image)
            # 按任意键退出
            cv2.waitKey(0)

alexnet.py

import tensorflow as tf
import numpy as np

# 卷积层,输入参数x, 卷积核的宽和长,步长的大小,卷积的数目,输出层的名字,是否补零,以及分层的数目
def convLayer(x, Kheight, Kwidth, strideX, strideY, numfilter, name, padding='SAME', groups=1):
    # 获得图片的通道数
    channels = int(x.get_shape()[-1])
    # 使用lambda构造卷积的函数
    conv = lambda a, b:tf.nn.conv2d(a, b, strides=[1, strideY, strideX, 1], padding=padding)
    # 在名字为name的环境下,进行卷积操作
    with tf.variable_scope(name) as scope:
        # 使用tf.get_variable初始化w
        w = tf.get_variable('w', shape=[Kheight, Kwidth, channels/groups, numfilter])
        # 初始化b
        b = tf.get_variable('b', shape=[numfilter])
        # 根据group的大小,如果group等于2,那么第三个维度就切分成两个数据
        # X的切分
        Xnew = tf.split(value=x, num_or_size_splits=groups, axis=3)
        # w的切分
        Wnew = tf.split(value=w, num_or_size_splits=groups, axis=3)
        # 对于各自的X,w进行分别的卷积操作
        featureMap = [conv(t1, t2) for t1, t2 in zip(Xnew, Wnew)]
        # 将各自卷积后的结果进行拼接,方便后续的池化和归一化操作
        mergeMap = tf.concat(featureMap, axis=3)
        # 添加偏置项
        out = tf.nn.bias_add(mergeMap, b)
        # 添加激活层函数, 名字为scope.name
        out = tf.nn.relu(out, name=scope.name)

        return out

# 最大值池化,输入参数为x,卷积的宽和长,卷积的步长,结果的名字,以及补零
def MaxPool(x, Kheight, Kwidth, strideX, strideY, name, padding='SAME'):
    # tf.nn.max_pool的参数,输入x, ksize表示卷积大小,strides表示步长,name表示返回值的名字,padding补零的方式
    return tf.nn.max_pool(x, ksize=[1, Kheight, Kwidth, 1], strides=[1, strideY, strideX, 1], name=name, padding=padding)

# 局部响应归一化
def LRN(x, R, alpha, beta, name, bias=1.0):
    # 在不同的filter上进行各个像素的归一化操作
    return tf.nn.lrn(x, depth_radius=R, alpha=alpha, beta=beta, name=name, bias=bias)

# 全连接操作,输入x, inputS表示输入层w的大小,outputS表示输出层w的大小,relu_flag表示是否卷积,name表示名字
def fcLayer(x, inputS, outputS, relu_flag, name):
   # 在name的范围下进行操作
    with tf.variable_scope(name) as scope:
        # 设置w的维度
        w = tf.get_variable('w', shape=[inputS, outputS])
        # 设置b的维度
        b = tf.get_variable('b', shape=[outputS])
        # 进行点乘在进行加偏置项操作
        out = tf.nn.xw_plus_b(x, w, b, name=scope.name)
        # 如果进行relu
        if relu_flag:
            # 使用tf.nn.relu进行relu操作
            return tf.nn.relu(out)
        else:
            return out
# 进行dropout,输入x,保留的大小,keep_prob,name表示名字
def dropout(x, keep_prob, name):

    return tf.nn.dropout(x, keep_prob=keep_prob, name=name)

class AlexNet(object):

    def __init__(self, x, keep_prob, numClass, skip, pathmodel='bvlc_alexnet.npy'):
        # 将输入的参数进行self操作
        self.x = x
        self.keep_prob = keep_prob
        self.numClass = numClass
        self.skip = skip
        # 参数的路径
        self.pathmodel = pathmodel
        # 进行模型框架的构建
        self.bulidModel()

    def bulidModel(self):
        # 进行第一层的卷积操作,卷积核的大小为11*11,步长为4*4, num_filter=96,name='conv1', padding='VALID'
        conv1 = convLayer(self.x, 11, 11, 4, 4, 96, 'conv1', 'VALID')
        # 进行第一层的最大值池化操作
        pool1 = MaxPool(conv1, 3, 3, 2, 2, 'pool1', 'VALID')
        # 进行局部响应归一化操作
        lrn1 = LRN(pool1, 2, 2e-5, 0.75, 'norm1')
        # 进行第二次的卷积操作,卷积核大小5*5,步长1,num_filter=256, 名字'conv2',进行分层卷积操作
        conv2 = convLayer(lrn1, 5, 5, 1, 1, 256, 'conv2', groups=2)
        # 进行第二层的最大值池化操作
        pool2 = MaxPool(conv2, 3, 3, 2, 2, 'pool2', 'VALID')
        # 进行局部响应归一化操作
        lrn2 = LRN(pool2, 2, 2e-5, 0.75, 'norm2')
        # 进行第三层的卷积操作,卷积核的大小为3*3, 步长为1*1,num_filter=384,name='conv3'
        conv3 = convLayer(lrn2, 3, 3, 1, 1, 384, 'conv3')
        # 进行第四层卷积操作,卷积核的大小为3*3,1*1,num_filter=384,进行分层操作
        conv4 = convLayer(conv3, 3, 3, 1, 1, 384, 'conv4', groups=2)
        # 进行第五层卷积操作,卷积核大小为3*3,步长为1*1,num_filter=256, name='conv5'进行分层操作
        conv5 = convLayer(conv4, 3, 3, 1, 1, 256, 'conv5', groups=2)
        # 进行第五层的池化操作
        pool5 = MaxPool(conv5, 3, 3, 2, 2, 'pool5', 'VALID')
        # 对于池化后的数据,进行维度的变换,将4维数据,转换为二维数据,以便进行后续的全连接操作
        fcIn = tf.reshape(pool5, [-1, 6*6*256])
        # 进行全连接操作,输入参数,输入层w的维度,输出层w的维度,是够进行relu操作,name='fc6'
        fc6 = fcLayer(fcIn, 6*6*256, 4096, True, 'fc6')
        # 进行dropout操作
        dropout1 = dropout(fc6, self.keep_prob, 'dropout1')
        # 进行第七层的全连接操作,输入,4096表示输入层w的维度,4096输出层w的维度
        fc7 = fcLayer(dropout1, 4096, 4096, True, 'fc7')
        # 进行dropout操作
        dropout2 = dropout(fc7, self.keep_prob, 'dropout2')
        # 最后一层进行类别预测得分的层,使用self.fc8为了score可以被外部调用
        self.fc8 = fcLayer(dropout2, 4096, self.numClass, True, 'fc8')

    # 将加载的参数根据层的名字,赋值给w和b参数
    def load_model(self, sess):
        # 进行npy文件的读取,使用.item()获得实际的值,这里的表示方式是列表,键为层数名,值为w和b
        dict_W = np.load(self.pathmodel, encoding='bytes').item()
        # 循环层数名
        for name in dict_W:
            # 如果层数名不在去除的名单里,这个可以用来进行参数的初始化,一部分参数不进行初始化,比如最后一层的finetune操作
            if name not in self.skip:
                # 在当前层的名字下,对参数进行复用
                with tf.variable_scope(name, reuse=True):
                    # 循环键中的参数值
                    for p in dict_W[name]:
                        # 如果参数的维度大于1,即为w
                        if len(p.shape) > 1:
                            # 使用tf.get_variable()获得值,使用trainable对参数实行冻结,.assign将读取的参数赋值给w
                            sess.run(tf.get_variable('w', trainable=False).assign(p))
                        else:
                            # 使用tf.get_variable获得参数,使用trainable对参数进行冻结,assign将读入数据赋值给b
                            sess.run(tf.get_variable('b', trainable=False).assign(p))

深度学习原理与框架-Alexnet(迁移学习代码)  1.sys.argv[1:](控制台输入的参数获取第二个参数开始) 2.tf.split(对数据进行切分操作) 3.tf.concat(对数据进行合并操作) 4.tf.variable_scope(指定w的使用范围) 5.tf.get_variable(构造和获得参数) 6.np.load(加载.npy文件)

收获:可以使用skip来筛选出需要下载的参数,这样自己可以构建最后一层的参数,或者最后几层的参数进行训练。

同样的,使用tf.get_variable('w', trainable=False) 将赋值的参数,进行冻结不让其进行训练。