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


    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

                                  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))

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

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

                 

  • 相关阅读:
    uwsgi
    protobuf c++ API
    memcached 第二篇----安装使用
    ice grid配置使用第二篇------实际使用
    ICE BOX 配置,使用----第一篇
    可视化资料收集
    Protocol Buffers
    ice grid 完整部署过程
    django组件之ajax
    关于Django在写小项目的一些小注意事项
  • 原文地址:https://www.cnblogs.com/my-love-is-python/p/10567016.html
Copyright © 2020-2023  润新知