程序项目代做,有需求私信(vue、React、Java、爬虫、电路板设计、嵌入式linux等)

第十六节,卷积神经网络之AlexNet网络实现(六)

上一节内容已经详细介绍了AlexNet的网络结构。这节主要通过Tensorflow来实现AlexNet。

这里做测试我们使用的是CIFAR-10数据集介绍数据集,关于该数据集的具体信息可以通过以下链接查看:https://blog.csdn.net/davincil/article/details/78793067下面粗略的介绍一下CIFAR-10数据集。

一 CIFAR-10数据集

1.1 CIFAR-10数据集介绍

CIFAR-10数据集由10类32x32的彩色图片组成,一共包含60000张图片,每一类包含6000图片。其中50000张图片作为训练集,10000张图片作为测试集。

CIFAR-10数据集被划分成了5个训练的batch和1个测试的batch,每个batch均包含10000张图片。测试集batch的图片是从每个类别中随机挑选的1000张图片组成的,训练集batch以随机的顺序包含剩下的50000张图片。不过一些训练集batch可能出现包含某一类图片比其他类的图片数量多的情况。训练集batch包含来自每一类的5000张图片,一共50000张训练图片。

数据集下载地址:http://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz

文件下载后,解压cifar-10-python.tar.gz,得到cifar-10-batches-py文件夹,打开该文件夹,我们会看到有如下文件:

其中每个文件的作用如下:

  • batches.meta     程序中不需要使用该文件;
  • data_batch_1     训练集的第一个batch,含有10000张图片;
  • data_batch_2     训练集的第二个batch,含有10000张图片;
  • data_batch_3     训练集的第三个batch,含有10000张图片;
  • data_batch_4     训练集的第四个batch,含有10000张图片;
  • data_batch_5     训练集的第五个batch,含有10000张图片;
  • readme.html       网页文件,程序中不需要使用该文件;
  • test_batch          测试集的batch,含有10000张图片;

上述文件结构中,每一个batch文件包含一个python的字典(dict)结构,结构如下:

  • b'data’                 是一个10000x3072的array,每一行的元素组成了一个32x32的3通道图片,共10000张;
  • b'labels’               一个长度为10000的list,对应包含data中每一张图片的 label;
  • b'batch_label'     这一份batch的名称;
  • b'filenames'        一个长度为10000的list,对应包含data中每一张图片的名称;

由于数据集比较大,在训练的时候如果把所有数据一次性加载到内存训练,会出现内容不足的问题,因此先从batch中读取所有图片的数据,以及每一张图片对应的标签,然后我们创建一个文件夹叫做CIFAR-10-data。

在这个文件夹下面创建train和test文件夹,然后在每个文件夹下面创建名称从0-9的文件夹,我们利用OpenCV把每一张图片保存在对应文件夹下面。

后面再创建两个文件,一个叫做CIFAR-10-test-label.pkl,另一个叫做CIFAR-10-train-label.pkl,均保存由如下元组:(测试集或训练集的图片路径,以及对应标签)组成的list集合。

1.2 数据集处理

datagenerator.py文件代码如下:

# -*- coding: utf-8 -*-
"""
Created on Wed Apr 11 14:51:27 2018

@author: Administrator
"""

'''
用于加载数据集合 
数据集下载地址:http://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
CIFAR-10数据集介绍:https://blog.csdn.net/davincil/article/details/78793067

一、CIFAR-10
CIFAR-10数据集由10类32x32的彩色图片组成,一共包含60000张图片,每一类包含6000图片。其中50000张图片作为训练集,10000张图片作为测试集。

CIFAR-10数据集被划分成了5个训练的batch和1个测试的batch,每个batch均包含10000张图片。
测试集batch的图片是从每个类别中随机挑选的1000张图片组成的,一共10000张测试图片,
训练集batch包含来自每一类的5000张图片,一共50000张训练图片。
训练集batch以随机的顺序包含剩下的50000张图片。
不过一些训练集batch可能出现包含某一类图片比其他类的图片数量多的情况。

'''

'''

文件下载之后,解压  主要包括以下文件
名称            作用
batches.meta     程序中不需要使用该文件
data_batch_1     训练集的第一个batch,含有10000张图片
data_batch_2     训练集的第二个batch,含有10000张图片
data_batch_3     训练集的第三个batch,含有10000张图片
data_batch_4     训练集的第四个batch,含有10000张图片
data_batch_5     训练集的第五个batch,含有10000张图片
readme.html     网页文件,程序中不需要使用该文件
test_batch     测试集的batch,含有10000张图片


上述文件结构中,每一个batch文件包含一个python的字典(dict)结构,结构如下:
名称              作用
b'data’          是一个10000x3072的array,每一行的元素组成了一个32x32的3通道图片,共10000张
b'labels’          一个长度为10000的list,对应包含data中每一张图片的label
b'batch_label' 这一份batch的名称
b'filenames'      一个长度为10000的list,对应包含data中每一张图片的名称

'''

import pickle
import numpy as np
import cv2
from skimage import io

class datagenerator(object):
    def __init__(self):
        pass
        
        
    def unpickle(self,filename):
        '''
        batch文件中真正重要的两个关键字是data和labels        
        反序列化出对象
        
        每一个batch文件包含一个python的字典(dict)结构,结构如下:
        名称              作用
        b'data’          是一个10000x3072的array,每一行的元素组成了一个32x32的3通道图片,共10000张
        b'labels’          一个长度为10000的list,对应包含data中每一张图片的label
        b'batch_label' 这一份batch的名称
        b'filenames'      一个长度为10000的list,对应包含data中每一张图片的名称        
        '''
        with open(filename,'rb')  as  f:
            #默认把字节转换为ASCII编码  这里设置encoding='bytes'直接读取字节数据  因为里面含有图片像素数据 大小从0-255 不能解码为ascii编码,因此先转换成字节类型 后面针对不同项数据再解码,转换为字符串        
            dic = pickle.load(f,encoding='bytes')     
        return dic
    
    
    def get_image(self,image):
        '''
        提取每个通道的数据,进行重新排列,最后返回一张32x32的3通道的图片
        
        在字典结构中,每一张图片是以被展开的形式存储(即一张32x32的3通道图片被展开成了3072长度的list),
        每一个数据的格式为uint8,前1024个数据表示红色通道,接下来的1024个数据表示绿色通道,最后的1024
        个通道表示蓝色通道。
        image:每一张图片的数据  数据按照R,G,B通道依次排列 长度为3072
        '''
        assert len(image) == 3072
        #对list进行切片操作,然后reshape
        r = image[:1024].reshape(32,32,1)
        g = image[1024:2048].reshape(32,32,1)
        b = image[2048:].reshape(32,32,1)
        
        #numpy提供了numpy.concatenate((a1,a2,...), axis=0)函数。能够一次完成多个数组的拼接。其中a1,a2,...是数组类型的参数
        #沿着某个轴拼接,默认为列方向(axis=0)
        img = np.concatenate((r,g,b),-1)
        return img
        
    
    def get_data_by_keyword(self,keyword,filelist=[],normalized=False,size=(32,32),one_hot=False):
        '''
        按照给出的关键字提取batch中的数据(默认是训练集的所有数据)
        
        args:
            keyword:'data’ 或 'labels’ 或  'batch_label' 或  'filenames' 表示需要返回的项
            filelist:list 表示要读取的文件集合
            normalized:当keyword = 'data',表示是否需要归一化
            size:当keyword = 'data',表示需要返回的图片的尺寸
            one_hot:当keyword = 'labels'时,one_hot=Flase,返回实际标签  True时返回二值化后的标签
            
        return:
            keyword = 'data' 返回像素数据
            keyword = 'labels' 返回标签数据
            keyword = 'batch_label' 返回batch的名称
            keyword = 'filenames' 返回图像文件名
                 
        '''
        
        #keyword编码为字节
        keyword = keyword.encode('ascii')
        assert keyword in [b'data',b'labels',b'batch_label',b'filenames']
        assert type(filelist) is list and len(filelist) != 0
        assert type(normalized) is bool
        assert type(size) is tuple or type(size) is list
                
        
        ret = []
        
             
        for i in range(len(filelist)):            
            #反序列化出对象
            dic = self.unpickle(filelist[i])
            
            if keyword == b'data':
                #b'data’          是一个10000x3072的array,每一行的元素组成了一个32x32的3通道图片,共10000张
                #合并成一个数组           
                for item in dic[b'data']:
                    ret.append(item) 
                print('总长度:',len(ret))
                
            elif keyword == b'labels':
                #b'labels’          一个长度为10000的list,对应包含data中每一张图片的label          
                #合并成一个数组               
                for item in dic[b'labels']:
                    ret.append(item) 
              
                
            elif keyword == b'batch_label':
                #b'batch_label' 这一份batch的名称           
                #合并成一个数组               
                for item in dic[b'batch_label']:
                    ret.append(item.decode('ascii'))    #把数据转换为ascii编码
            
            else:
                #b'filenames'      一个长度为10000的list,对应包含data中每一张图片的名称                    
                #合并成一个数组               
                for item in dic[b'filenames']:
                    ret.append(item.decode('ascii'))    #把数据转换为ascii编码
                
            
        if keyword == b'data':                      
            if normalized == False:
                array = np.ndarray([len(ret),size[0],size[1],3],dtype = np.float32)                
                #遍历每一张图片数据
                for i in range(len(ret)):
                    #图像进行缩放
                    array[i] = cv2.resize(self.get_image(ret[i]),size)
                return array
            
            else:
                array = np.ndarray([len(ret),size[0],size[1],3],dtype = np.float32)                
                #遍历每一张图片数据
                for i in range(len(ret)):
                    array[i] = cv2.resize(self.get_image(ret[i]),size)/255
                return array
            pass
        
        elif keyword == b'labels':
            #二值化标签
            if one_hot == True:
                #类别
                depth = 10
                m = np.zeros([len(ret),depth])
                for i in range(len(ret)):
                    m[i][ret[i]] = 1
                return m
            pass
        #其它keyword直接返回
        return ret
      
        
    
import os 
import pickle

def  save_images():
    '''
    报CIFAR-10数据集图片提取出来保存下来  
    1.创建一个文件夹 CIFAR-10-data 包含两个子文件夹test,train
    2.在文革子文件夹创建10个文件夹 文件名依次为0-9  对应10个类别
    3.训练集数据生成bmp格式文件,存在对应类别的文件下    
    4.测试集数据生成bmp格式文件,存在对应类别的文件下  
    5 生成两个文件train_label.pkl,test_label.pkl 分别保存相应的图片文件路径以及对应的标签
    '''
    
    #根目录
    root = 'CIFAR-10-data'
    
    #如果存在该目录 说明数据存在
    if os.path.isdir(root):
        print(root+'目录已经存在!')
        return 
    
    '''
    如果文件夹不存在 创建文件夹 
    '''
    #'data'目录不存在,创建目录
    os.mkdir(root)
    
    #创建文件失败
    if not os.path.isdir(root): 
        print(root+'目录创建失败!')
        return 
    
    #创建'test'和'train'目录  以及子文件夹
    train = os.path.join(root,'train')
    os.mkdir(train)
    if os.path.isdir(train):
        for i in range(10):
            name = os.path.join(train,str(i))
            os.mkdir(name)
            
    test = os.path.join(root,'test')
    os.mkdir(test)
    if os.path.isdir(test):
        for i in range(10):
            name = os.path.join(test,str(i))
            os.mkdir(name)
            
    
    
    '''
    把训练集数据转换为图片
    '''
    data_dir = 'cifar-10-batches-py'       #数据所在目录
    
    filelist = []
    for i in range(5):
        name = os.path.join(data_dir,str('data_batch_%d'%(i+1)))
        filelist.append(name)
        
    data = datagenerator()
    #获取训练集数据
    train_x = data.get_data_by_keyword('data',filelist,
                                           normalized=True,size=(32,32)) 
        
    #标签
    train_y = data.get_data_by_keyword('labels',filelist)
                                           
    
    #读取图片文件名
    train_filename  = data.get_data_by_keyword('filenames',filelist)
                                            
   
    #保存训练集的文件名和标签
    train_file_labels  = [] 
    
    #保存图片
    for i in  range(len(train_x)):
        #获取图片标签
        y = int(train_y[i])
        
        #文件保存目录
        dir_name = os.path.join(train,str(y))
  
        #获取文件名
        file_name = train_filename[i]       
       
        #文件的保存路径
        file_path = os.path.join(dir_name,file_name)
     
        #保存图片
        io.imsave(file_path,train_x[i])
        
        #追加第i张图片路径和标签   (文件路径,标签)
        train_file_labels.append((file_path,y))
        
        if i % 1000 == 0:
            print('训练集完成度{0}/{1}'.format(i,len(train_x)))
    
    for i in range(10):
        print('训练集前10张图片:',train_file_labels[i])
        
    #保存训练集的文件名和标签
    with open('CIFAR-10-train-label.pkl','wb') as f:      
        pickle.dump(train_file_labels,f)
    
    print('训练集图片保存成功!\n')
    
    '''
    把测试集数据转换为图片
    '''
    filelist = [os.path.join(data_dir,'test_batch')]
     
    #获取训练集数据 数据标准化为0-1之间
    test_x = data.get_data_by_keyword('data',filelist,
                                           normalized=True,size=(32,32)) 
        
    #标签
    test_y = data.get_data_by_keyword('labels',filelist)
                                           
    
    #读取图片文件名
    test_filename = data.get_data_by_keyword('filenames',filelist)
                                          

    #保存测试卷的文件名和标签    
    test_file_labels  = [] 
     
    #保存图片
    for i in  range(len(test_x)):
        #获取图片标签
        y = int(test_y[i])
        
        #文件保存目录
        dir_name = os.path.join(test,str(y))
        
        #获取文件名
        file_name = test_filename[i]     
        
        #文件的保存路径
        file_path = os.path.join(dir_name,file_name)
        
        #保存图片  这里要求图片像素值在-1-1之间,所以在获取数据的时候做了标准化
        io.imsave(file_path,test_x[i])
        
        #追加第i张图片路径和标签  (文件路径,标签)
        test_file_labels.append((file_path,y))
        
        if i % 1000 == 0:
            print('测试集完成度{0}/{1}'.format(i,len(test_x)))
    
    print('测绘集图片保存成功!\n')
    
    #保存测试卷的文件名和标签
    with open('CIFAR-10-test-label.pkl','wb') as f:        
        pickle.dump(test_file_labels,f)   
    
    for i in range(10):
        print('测试集前10张图片:',test_file_labels[i])       
  

def load_data():
    '''
    加载数据集
    返回训练集数据和测试卷数据    
    training_data 由(x,y)元组组成的list集合
            x:图片路径
            y:对应标签
    '''
    #加载使用的训练集文件名和标签 [(文件路径,标签),....]
    with open('CIFAR-10-train-label.pkl','rb') as f:
        training_data = pickle.load(f)

        
    #加载使用的测试集文件名和标签
    with open('CIFAR-10-test-label.pkl','rb') as f:
        test_data = pickle.load(f)
    
    return training_data,test_data



def get_one_hot_label(labels,depth):
    '''
    把标签二值化  返回numpy.array类型
    
    args:
        labels:标签的集合
        depth:标签总共有多少类
    '''    
    m = np.zeros([len(labels),depth])
    for i in range(len(labels)):
        m[i][labels[i]] = 1
    return m



def get_image_data_and_label(value,image_size='NONE',depth=10,one_hot = False):
    '''
    获取图片数据,以及标签数据 注意每张图片维度为 n_w x n_h x n_c
    
    args:
        value:由(x,y)元组组成的numpy.array类型
            x:图片路径
            y:对应标签
        image_size:图片大小 'NONE':不改变图片尺寸 
        one_hot:把标签二值化
        depth:数据类别个数
    '''
    #图片数据集合
    x_batch = []
    #图片对应的标签集合
    y_batch = []    
    #遍历每一张图片
    for image in value:      
        if image_size == 'NONE':
            x_batch.append(cv2.imread(image[0])/255)    #标准化0-1之间
        else:
            x_batch.append(cv2.resize(cv2.imread(image[0]),image_size)/255)
        y_batch.append(image[1])    
        
    if one_hot == True:
        #标签二值化
        y_batch = get_one_hot_label(y_batch,depth)
    return  np.asarray(x_batch,dtype=np.float32),np.asarray(y_batch,dtype=np.float32)
      
'''
测试 保存所有图片
'''
save_images()

save_image()函数执行上面所述的功能:

  1. 创建一个文件夹 CIFAR-10-data 包含两个子文件夹test,train;
  2. 在子文件夹创建10个文件夹 文件名依次为0-9,对应10个类别;
  3. 训练集数据生成bmp格式文件,存在对应类别的文件下;
  4. 测试集数据生成bmp格式文件,存在对应类别的文件下;
  5. 生成两个文件train_label.pkl,test_label.pkl 分别保存相应的图片文件路径以及对应的标签;

执行完save_image()函数,会生成如下文件:

 

  

   

 二 使用传统神经网络训练

2.1 传统神经网络

在使用AlexNet网络进行训练之前,我们先使用传统network进行训练,这里我们设置网络为4层,包括输入层在内,每一层神经元个数如下3072,7200,1024,10。

我们在训练的时候,每次随机读取batch_size大小的图片数据进行训练。

传统network的实现,我是通过定义一个单独的类来完成该功能。network.py文件代码如下

# -*- coding: utf-8 -*-
"""
Created on Mon Apr  2 10:32:10 2018

@author: Administrator
"""

'''
定义一个network类,实现全连接网络
'''

import    datagenerator 
import os
from tensorflow.python import pywrap_tensorflow


def get_one_hot_label(labels,depth):
    '''
    把标签二值化
    
    args:
        labels:标签的集合
        depth:标签总共有多少类
    '''    
    m = np.zeros([len(labels),depth])
    for i in range(len(labels)):
        m[i][labels[i]] = 1
    return m


import tensorflow as tf
import numpy as np
import random
import pickle


class network(object):   
    '''
    全连接神经网络
    '''
    def __init__(self,sizes,param_path= None):     
        '''
        注意程序中op变量只需要初始化一遍就可以,在fit()中初始化
        
        args:
            sizes:list传入每层神经元个数
            param_path:是否从指定文件加载模型,None:重新训练  否则指定模型路径 必须指定路径./或者绝对路径
        '''
        #保存参数
        self.__sizes = sizes
        #神经网络每一层的神经元个数数组类型
        self.sizes = tf.placeholder(tf.int64,shape=[1,len(sizes)])
        #计算神经网络层数 包括输入层
        self.num_layer = tf.size(self.sizes)

        #输入样本和输出类别变量
        self.x_ = tf.placeholder(tf.float32,shape=[None,sizes[0]])
        self.y_ = tf.placeholder(tf.float32,shape=[None,sizes[-1]])
         
        #设置tensorflow对GPU使用按需分配
        config = tf.ConfigProto()
        config.gpu_options.allow_growth = True        
        self.sess = tf.InteractiveSession(config=config)
                
        
        file_exist = False
        #如果已经存在保存的模型 加载之前保存的w和b
        if not param_path is None:  
              if os.path.isfile(param_path): 
                  with open(param_path,'rb') as f:
                      dic = pickle.load(f)
                      weights = dic['weightes']
                      biases = dic['biases']                      
                      file_exist = True
        
        if file_exist:
            #使用保存的数据初始化  第i层和i+1层之间的权重向量 
            self.weights = [self.weight_variable(shape=(x,y),value = weights[i]) for x,y,i in  zip(sizes[:-1],sizes[1:],range(len(sizes)-1))]
            #使用保存的数据初始化  第i层的偏置向量  i=1...num_layers 注意不可以设置shape=(x,1)
            self.biases = [self.bias_variable(shape=[x,],value = biases[i]) for x,i in zip(sizes[1:],range(len(sizes)-1))]
            print('成功加载参数数据!') 
        else:
             #使用高斯正态分布初始化  第i层和i+1层之间的权重向量 
            self.weights = [self.weight_variable(shape=(x,y)) for x,y in  zip(sizes[:-1],sizes[1:])]
            #使用高斯正态分布初始化  第i层的偏置向量  i=1...num_layers 注意不可以设置shape=(x,1)
            self.biases = [self.bias_variable(shape=[x,]) for x in sizes[1:]]
            print('重新初始化模型参数!')
        

        '''这一段代码是使用tensorflow Saver对象保存 但是在取数据时候总是失败,因此不再使用这中方法
        #如果已经存在保存的模型 加载之前保存的w和b
        if not model_path is None:     
            
            file = model_path+'.meta'
            #print(file)
            #文件存在 则直接加载
            if os.path.isfile(file):                                        
                #加载以前保存的网络  将保存在.meta文件中的图添加到当前的图中
                self.new_saver = tf.train.import_meta_graph(file)
                #从指定目录下获取最近一次检查点                    
                self.new_saver.restore(self.sess,tf.train.latest_checkpoint(os.path.dirname(file))) 
                #使用加载的模型
                graph = tf.get_default_graph()
                #恢复w和b
                self.weights = [graph.get_tensor_by_name( 'network_w'+str(i)+':0')  for i in range(1,len(sizes))]               
                self.biases = [graph.get_tensor_by_name( 'network_b'+str(i)+':0')  for i in range(1,len(sizes))]                                

                print('成功从模型恢复数据!')                    
                sh = [self.sess.run(i).shape for i in self.weights]
                print('权重维度:',sh)
            
                file_exist = True
        
        #不存在 再新创建
        if not file_exist:
            #随机初始化权重  第i层和i+1层之间的权重向量 
            self.weights = [self.weight_variable(shape=(x,y),name='network_w'+str(i)) for x,y,i in  zip(sizes[:-1],sizes[1:],range(1,len(sizes)))]
            #随机初始化偏置  第i层的偏置向量  i=1...num_layers 注意不可以设置shape=(x,1)
            self.biases = [self.bias_variable(shape=[x,],name='network_b'+str(i)) for x,i in zip(sizes[1:],range(1,len(sizes)))]
            print('重新初始化模型参数!')
                
                        
        #创建Saver op用于保存新的训练参数 指定保存哪些变量,如果全部保存,在恢复时,会有错误 主要是由于偏置和权重我保存在了一个dict中引起的
        param =[w for w in  self.weights]
        for i in self.biases:
            param.append(i)
        self.saver = tf.train.Saver(param)'''
    
    def weight_variable(self,shape,value=None,name=None):
        '''
        初始化权值
        '''
        if value is None:
            #使用截断式正太分布初始化权值   截断式即在正态分布基础上加以限制,以使生产的数据在一定范围上
            value = tf.truncated_normal(shape,mean=0.0,stddev= 1.0/shape[0])    #方差为1/nin   
                    
        if name is None:
            return tf.Variable(value)
        else:
            return tf.Variable(value,name=name)

    def bias_variable(self,shape,value=None,name=None):
        '''
        #初始化偏重
        '''
        if value is None:
            value =  tf.truncated_normal(shape,mean=0.0,stddev= 1.0/shape[0])    #方差为1/nin
            
        if name is None:
            return tf.Variable(value)
        else:
            return tf.Variable(value,name=name)
    
    def feedforward(self,x):                 
        '''
        构建阶段:前向反馈
        x:变量op,tf.placeholder()类型变量
        返回一个op
        '''
        #计算隐藏层
        output = x
        for i in range(len(self.__sizes)-1):
            b = self.biases[i]
            w = self.weights[i]
            if i != len(self.__sizes)-2 :
                output =  tf.nn.relu(tf.matmul(output,w) + b)
            else:
                output = tf.nn.softmax(tf.matmul(output,w) + b)
        return output
           
    def fit(self,training_data,learning_rate=0.001,batch_size=64,epoches=10):
         '''
         训练神经网络
         training_data (x,y)元祖组成的list
             x:训练集图片数据路径
             y:训练集样本对应的标签
         learning_rate:学习率
         batch_size:批量大小
         epoches:迭代轮数
         '''                        
         #计算输出层
         output = self.feedforward(self.x_)
         
         #代价函数 J =-(Σy.logaL)/n    .表示逐元素乘
         cost = tf.reduce_mean( -tf.reduce_sum(self.y_*tf.log(output),axis = 1))
         
         #求解
         train = tf.train.AdamOptimizer(learning_rate).minimize(cost)
         
         #使用会话执行图     
        
         #初始化变量 必须在train之后 变量出事后化之后才可以显示相关属性
         self.sess.run(tf.global_variables_initializer())  
         sh = [self.sess.run(i).shape for i in self.weights]
         print('权重维度:',sh)
         
         #训练集长度
         n_train =len(training_data)
         
         #开始迭代
         for i in range(epoches): 
            #打乱训练集
            random.shuffle(training_data)
            #分组
            mini_batchs = [training_data[k:k+batch_size] for k in range(0,n_train,batch_size)]            
            
            #遍历每一个mini_batch
            for mini_batch in mini_batchs:                        
                x_batch,y_batch = datagenerator.get_image_data_and_label(mini_batch,one_hot=True)    
                #每张图片数据展开正一列
                x_batch = x_batch.reshape(len(mini_batch),-1)
                train.run(feed_dict={self.x_:x_batch,self.y_:y_batch})   
                        
            '''
            #计算每一轮迭代后在整个训练集的误差 并打印
            '''
            train_cost_sum = []
            train_accuracy_sum = []
            #遍历每一个mini_batch
            for mini_batch in mini_batchs:                
                train_cost = cost.eval(feed_dict={self.x_:x_batch,self.y_:y_batch}) 
                train_cost_sum.append(train_cost)
                train_accuracy = self.accuracy(x_batch,y_batch) 
                train_accuracy_sum.append(train_accuracy)
            print('Epoch {0} Training set cost {1} accuracy {2}:'.format(i,np.mean(train_cost),np.mean(train_accuracy)))
         
                
    def predict(self,test_x):        
        '''
        对输入test_x样本进行预测(图片数据)   nxm维   n为样本个数 m为数据长度
        '''
        output = self.feedforward(self.x_)
        #使用会话执行图
        return output.eval(feed_dict={self.x_:test_x})
             
     
    def accuracy(self,x,y):
        '''
        返回值准确率
        x:测试样本集合(图片数据) nxm维   n为样本个数 m为数据长度
        y:测试类别集合 也是二值化的标签
        '''
        output = self.feedforward(self.x_)
        correct = tf.equal(tf.argmax(output,1),tf.argmax(self.y_,1))       #返回一个数组 表示统计预测正确或者错误 
        accuracy = tf.reduce_mean(tf.cast(correct,tf.float32))             #求准确率        
        #使用会话执行图
        return accuracy.eval(feed_dict={self.x_:x,self.y_:y})
    
    def cost(self,x,y):
        '''
        计算代价值
        x:样本集合(图片数据) nxm维   n为样本个数 m为数据长度
        y:类别集合  也是二值化的标签
        '''
        #计算输出层
        output = self.feedforward(self.x_)         
        #代价函数 J =-(Σy.logaL)/n    .表示逐元素乘
        cost = tf.reduce_mean( -tf.reduce_sum(self.y_*tf.log(output),axis = 1))
        #使用会话执行图
        return cost.eval(feed_dict={self.x_:x,self.y_:y})
    
    def remove_model_file(self,file):
        '''
        删除保存的模型文件
        删除成功返回True 否则返回 False
        
        args:
            file:模型文件名
        '''    
        path = [file+item for item in ('.index','.meta')]
        path.append(os.path.join( os.path.dirname(path[0]),'checkpoint'))
        for f in path:
            if os.path.isfile(f):
                os.remove(f)            
        ret = True
        #检查文件是否存在
        for f in path:
            if os.path.isfile(f):
                ret = False
        return ret
    
            
    def save_model(self,file):
        '''
        保存模型 
        
        args:
            file:保存文件名
        '''
        '''
        #先删除之前的模型数据                
        if self.remove_model_file(file) == True:
            self.saver.save(self.sess,file)
            print('模型保存成功!')'''
            
        #把参数序列化并保存
        weights = [item.eval() for item in self.weights]
        biases = [item.eval() for item in self.biases]
        dic = {'weightes':weights,'biases':biases}
        with open(file,'wb') as f:
            pickle.dump(dic,f)
            print('模型参数保存成功!')

    def  global_variables_initializer(self):
        '''
        全局张量初始化,由于全局变量只在fit()训练时进行初始化,如果我们直接加载已经训练好的数据
        就不需要调用fit()函数,因此只有收到调用这个函数就行初始化权重和偏置以及其他张量
        然后才可以执行图 如accuracy()等等
        '''
        self.sess.run(tf.global_variables_initializer())  
       

然后我们使用该网络进行测试,并且把测试后的权重和偏置保存在指定文件,下次可以使用该数据初始化,然后继续训练

  • 我们先读取CIFAR-10-train-label.pkl,获取每张的训练图片路径以及标签组成的元组,保存在一个字典中;
  • 我们先把该字典打乱,然后选择batch_size张图片,读取图片,以及one_hot(二值化)标签,组成一个mini_batch;
  • 使用mini_batch进行训练;
  • 迭代字典中的所有数据,迭代完一轮,我们继续迭代,直至到我们设置的迭代轮数;

在这里有个地方需要注意:由于我们每次都是从指定路径读取图片,因此速度比较慢,其实我们可以利用get_data_by_keyword()函数一次性读取所有数据和标签,然后保存在内存中,然后每次选择batch_size数据进行训练。这样就避免了每次从硬盘读取图片数据。这里我们取数据的时候可以先设置一个训练集大小的索引字典,然后打乱该字典,我们每次把batch_size个字典元素,传给数据,这样我们就可以达到随机取数据的目的。

# 生成并打乱训练集的顺序。
indices = np.arange(50000)
random.shuffle(indices)

2.2 测试代码

测试代码如下:

# -*- coding: utf-8 -*-
"""
Created on Wed Apr 11 16:42:09 2018
@author: Administrator
"""
'''
用于训练network网络
由于内存有限,不能一次读取所有的数据,因此采用每次读取小批量的图片
1.在datagenerator.py文件中 把所有图片保存成bmp格式文件
2.存储一个dict字典,每个元素为 (图片的相对路径,标签),将这个字典序列化保存到文件
3.读取该文件,把元组顺序打乱,分成多组,每次加载mini_batch个图片进行训练
'''

import  tensorflow as tf
from    alexnet  import alexnet
import    datagenerator 
import numpy as np
#import cv2
import random
import network
import os
def remove_model_file(file):
    '''
    删除保存的模型文件
    删除成功返回True 否则返回 False
    
    args:
        file:模型文件名
    '''    
    path = [file+item for item in ('.index','.meta')]
    path.append(os.path.join( os.path.dirname(path[0]),'checkpoint'))
    for f in path:
        if os.path.isfile(f):
            os.remove(f)            
    ret = True
    #检查文件是否存在
    for f in path:
        if os.path.isfile(f):
            ret = False
    return ret


def network_main():
    '''
    使用network网络测试
    由于数据量比较大,图片不能一次全部加载进来,因此采用分批读取图片数据
    '''
    
    '''
    一 加载数据
    '''
    training_data,test_data = datagenerator.load_data()
    param_path = './network_param/network_param.pkl'                   #模型参数保存所在文件
    learning_rate = 1e-4                                  #学习率
    training_epoches = 10                                 #训练轮数
    batch_size = 256                                      #小批量大小
    
    '''
    二 创建网络
    '''
    #如果已经存在训练好的数据,直接加载初始化
    nn = network.network([3072,7200,1024,10],param_path=param_path)
    
    '''
    三 开始训练
    '''
    nn.fit(training_data,learning_rate=learning_rate,batch_size=batch_size,epoches=training_epoches)
    
    nn.save_model(param_path)                    #保存参数
              
    '''
    四 校验
    '''
    mini_batchs = [test_data[k:k+batch_size] for k in range(0,len(test_data),batch_size)]     
    test_accuracy_sum = []            
    
    #如果不训练网络,需要手动初始化张量
    #nn.global_variables_initializer()
    
    for mini_batch in mini_batchs:
        x_batch,y_batch = datagenerator.get_image_data_and_label(mini_batch,one_hot=True) 
        #每张图片数据展开正一列
        x_batch = x_batch.reshape(len(mini_batch),-1)
        test_accuracy_sum.append(nn.accuracy(x_batch,y_batch))    
        
    print('准确率:',np.mean(test_accuracy_sum))
'''
测试
'''
if __name__ == '__main__':
    network_main()

运行结果如下,我们可以看到迭代10次后,在测试集的准确率大概为52%,如果我们使用卷积神经网络的话或者迭代次数更多一些的话,准确率可能会更高,你也可以尝试使用LeNet进行训练,详情可以参考:Tensorflow深度学习之二十一:LeNet的实现(CIFAR-10数据集)https://blog.csdn.net/davincil/article/details/78794044

三 使用AlexNet网络训练

3.1 AlexNet网络

按照上一节讲的内容定义,alexnet网络结构,alexnet.py文件代码如下:

# -*- coding: utf-8 -*-
"""
Created on Wed Apr 11 10:48:31 2018

@author: Administrator
"""

'''
自己实现一个标准的AlexNet网络
参考链接:https://blog.csdn.net/OliverkingLi/article/details/73849228
https://blog.csdn.net/DaVinciL/article/details/78888605
'''
import tensorflow as tf


def conv(x,filter_height,filter_width,out_channels,stride_y, stride_x, name,
         padding='SAME'):
    '''
    定义一个卷积层 
    Create a convolution layer.
    Adapted from: https://github.com/ethereon/caffe-tensorflow
    
    Args:
        x:输入图片集合 维度为[batch, in_height, in_width, in_channels] 
        weights:共享权重[filter_height, filter_width, in_channels, out_channels]
            filter_height : 过滤器高度
            filter_width:  过滤器宽度
            in_channels:   输入通道数 
            out_channels   过滤器个数
        biases:共享偏置 [out_channels]
        stride_x:      x轴步长
        stride_y:      y轴步长
        name:        卷积层名字
        padding:      卷基层填充方式 'VALID'或者'SAME'
    '''
    #获取输入通道数
    in_channels = int(x.get_shape()[-1])
    
    with tf.name_scope(name) as scope:
        #搜索变量没有就创建,有会报错 get_variable创建的变量不受name_scope的影响
        weights = tf.get_variable(scope+'w', shape=[filter_height,
                                                    filter_width,
                                                    in_channels,
                                                    out_channels],
                                                dtype = tf.float32,
                                 initializer = tf.contrib.layers.xavier_initializer_conv2d())
        
        '''
        或者写成
        shape=[filter_height,filter_width,in_channels,out_channels]
        weights = tf.get_variable(scope+'w', shape=shape,dtype = tf.float32)
        '''
        #Variable创建变量,自动检测有没有重命名,有就自行处理
        biases = tf.Variable(tf.constant(0.0,shape=[out_channels]),dtype=tf.float32,trainable=True,name=scope+'b')
        
        #注意这里的strides每个元素分别与[batch, in_height, in_width, in_channels]对应
        conv = tf.nn.conv2d(x, weights,strides=[1, stride_y, stride_x, 1],
                                         padding=padding)
        # Add biases
        bias = tf.nn.bias_add(conv, biases)

        # Apply relu function
        relu = tf.nn.relu(bias, name=scope)
        return relu

def max_pool(x, filter_height, filter_width, stride_y, stride_x, name,
             padding='SAME'):
    '''
    定义一个池化层 最大下采样操作     
    
    Args:
        x:输入图片集合 维度为[batch, in_height, in_width, in_channels] 
        filter_height : 滑动窗口高度
        filter_width:  滑动窗口宽度
        stride_x:      x轴步长
        stride_y:      y轴步长
        name:         池化层名字
        padding:      填充方式 'SAME'或者'VALID'
    '''
    return tf.nn.max_pool(x, ksize=[1, filter_height, filter_width, 1],
                          strides=[1, stride_y, stride_x, 1],
                          padding=padding, name=name)


def fc(x, num_out,name, relu=True):
    '''
    全连接网络
    '''
    num_in = int(x.get_shape()[-1])
    with tf.name_scope(name) as scope:

        # Create tf variables for the weights and biases
        weights = tf.get_variable(scope+'w', shape=[num_in, num_out],
                                             dtype = tf.float32,
                                 initializer = tf.contrib.layers.xavier_initializer_conv2d())
        '''
        或者写成
        shape=[num_in, num_out]
        weights = tf.get_variable(scope+'w', shape=shape,dtype = tf.float32)
        
        '''
        
        biases = tf.Variable(tf.constant(0.0,shape=[num_out]),dtype=tf.float32,trainable=True,name=scope+'b')                                 


        if relu:
            act = tf.nn.xw_plus_b(x, weights, biases)
            # Apply ReLu non linearity
            relu = tf.nn.relu(act, name=scope)
            return relu
        else:
            act = tf.nn.xw_plus_b(x, weights, biases, name=scope)
            return act
    

def lrn(x, radius, alpha, beta, name, bias=1.0):
    '''
    局部相应归一化操作
    参考论文:http://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf
    Create a local response normalization layer.
    
    Args:
        depth_radius:归一化窗口的半径长度 一般设置为5 也是 论文中的n/2的值
        alpha:超参数 一般设置为1e-4
        beta:指数 一般设置为0.5
        bias:偏置 一般设置为1.0
        name:lrn层名字
    '''    
    return tf.nn.local_response_normalization(x, depth_radius=radius,
                                              alpha=alpha, beta=beta,
                                              bias=bias, name=name)


def dropout(x, keep_prob):
    '''
    弃权操作
    
    Args:
        x:激活函数之后的输出
        keep_prob:弃权概率 即每个神经元被保留的概率
    '''
    return tf.nn.dropout(x, keep_prob)


'''
初始化权值和偏重
为了创建这个模型,我们需要创建大量的权重和偏置项。这个模型中的权重在初始化时应该加入少量的噪声来
打破对称性以及避免0梯度。由于我们使用的是ReLU神经元,因此比较好的做法是用一个较小的正数来初始化
偏置项,以避免神经元节点输出恒为0的问题(dead neurons)。为了不在建立模型的时候反复做初始化操作
,我们定义两个函数用于初始化。
'''
def weight_variable(shape):
    #使用正太分布初始化权值
    initial = tf.truncated_normal(shape,dtype=tf.float32,mean=0,stddev=0.1)    #标准差为0.1
    return tf.Variable(initial)



def bias_variable(shape):
    initial = tf.constant(0.0,shape=shape)
    return tf.Variable(initial,dtype=tf.float32)


def print_dimension(t):
    '''
    输出指定张量的维度
    '''
    print(t.op.name,'',t.get_shape().as_list())


class alexnet(object):
    '''
    定义一个类 实现AlexNet网络
    '''

    def __init__(self, n_classes):
        '''
        Create the graph of the AlexNet model.

        Args:
            x: 输入张量的占位符 Placeholder for the input tensor.              
            keep_prob: 占位符 每个神经元保留的概率 Dropout probability.                                                                
        '''
        # 初始化参数   
            #占位符输入
        self.input_x = tf.placeholder(tf.float32,shape=[None,227,227,3],name='input_x')
        self.input_y = tf.placeholder(tf.float32,shape=[None,n_classes],name='input_y')
        self.keep_prob = tf.placeholder(tf.float32,name='keep_prob')        
        self.n_classes = n_classes

        self.out = self.create()
        
        
    def  initial_weights_biases(self):
        '''
        初始化权重和偏置
        
        '''          
        #标砖AlexNet参数
        weights = {
                'wc1': weight_variable([11,11,3,96]),
                'wc2': weight_variable([5,5,96,256]),
                'wc3': weight_variable([3,3,256,384]),
                'wc4': weight_variable([3,3,384,384]),
                'wc5': weight_variable([3,3,384,256]),
                'wfc6': weight_variable([6*6*256,4096]),
                'wfc7': weight_variable([4096,4096]),
                'wfc8': weight_variable([4096,10]),                                    
                }
        biases = {
                'bc1':bias_variable([96]),
                'bc2':bias_variable([256]),
                'bc3':bias_variable([384]),
                'bc4':bias_variable([384]),
                'bc5':bias_variable([256]),
                'bfc6':bias_variable([4096]),
                'bfc7':bias_variable([4096]),
                'bfc8':bias_variable([10]),
                }
        '''
        #为了加快训练速度 简化网络
        weights = {
                'wc1': weight_variable([11,11,3,64]),
                'wc2': weight_variable([5,5,64,192]),
                'wc3': weight_variable([3,3,192,384]),
                'wc4': weight_variable([3,3,384,384]),
                'wc5': weight_variable([3,3,384,256]),
                'wfc6': weight_variable([6*6*256,4096]),
                'wfc7': weight_variable([4096,4096]),
                'wfc8': weight_variable([4096,10]),                                    
                }
        biases = {
                'bc1':bias_variable([64]),
                'bc2':bias_variable([192]),
                'bc3':bias_variable([384]),
                'bc4':bias_variable([384]),
                'bc5':bias_variable([256]),
                'bfc6':bias_variable([4096]),
                'bfc7':bias_variable([4096]),
                'bfc8':bias_variable([10]),
                }
        '''
        return weights,biases
        

    def create(self):
        '''
        Create the network graph.
        创建AlexNet网络 返回网络输出的张量(没有经过正激函数)
        共有8层网络  5层卷积测,3个全连接网络
        '''
        # 1st Layer: Conv (w ReLu) -> Lrn -> Pool                
        self.conv1 = conv(self.input_x,filter_height=11,filter_width=11,out_channels=96,stride_y=4, stride_x=4, padding='VALID', name='conv1')
        print_dimension(self.conv1)
        self.norm1 = lrn(self.conv1, radius=4, alpha=1e-4, beta=0.75,  name='norm1')
        self.pool1 = max_pool(self.norm1, filter_height=3, filter_width=3, stride_y=2, stride_x=2, padding='VALID', name='pool1')
        print_dimension(self.pool1)

        # 2nd Layer: Conv (w ReLu)  -> Lrn -> Pool with 2 groups                    
        self.conv2 = conv(self.pool1,5,5,256, 1, 1, name='conv2')
        print_dimension(self.conv2)
        self.norm2 = lrn(self.conv2, radius=4, alpha=1e-4, beta=0.75, name='norm2')            
        self.pool2 = max_pool(self.norm2, 3, 3, 2, 2, padding='VALID', name='pool2')
        print_dimension(self.pool2)
        
        # 3rd Layer: Conv (w ReLu)   
        self.conv3 = conv(self.pool2,3,3,384, 1, 1, name='conv3')
        print_dimension(self.conv3)

        # 4th Layer: Conv (w ReLu) splitted into two groups              
        self.conv4 = conv(self.conv3,3,3,384, 1, 1, name='conv4')
        print_dimension(self.conv4)

        # 5th Layer: Conv (w ReLu) -> Pool splitted into two groups
        self.conv5 = conv(self.conv4,3,3,256, 1, 1, name='conv5')
        print_dimension(self.conv5)
        self.pool5 = max_pool(self.conv5, 3, 3, 2, 2, padding='VALID', name='pool5')
        print_dimension(self.pool5)

        # 6th Layer: Flatten -> FC (w ReLu) -> Dropout           
        flattened = tf.reshape(self.pool5, [-1, 6*6*256])
        self.fc6 = fc(flattened, 4096, name='fc6')
        print_dimension(self.fc6)
        self.dropout6 = dropout(self.fc6, self.keep_prob)

        # 7th Layer: FC (w ReLu) -> Dropout            
        self.fc7 = fc(self.dropout6,4096, name='fc7')
        print_dimension(self.fc7)
        self.dropout7 = dropout(self.fc7, self.keep_prob)

        # 8th Layer: FC and return unscaled activations
        self.out = fc(self.dropout7,self.n_classes, relu=False, name='out')
        print_dimension(self.out)
            
            
        #计算交叉熵损失函数        
        with tf.name_scope('cost'):
               self.cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits = self.out,labels = self.input_y))
        
        #准确率
        with tf.name_scope('accuracy'):
            #tf.argmax(output,1)  按行统计最大值得索引
            self.correct = tf.equal(tf.argmax(self.out,1),tf.argmax(self.input_y,1))       #返回一个数组 表示统计预测正确或者错误 
            self.accuracy = tf.reduce_mean(tf.cast(self.correct,tf.float32))      #求准确率
        
        #use softmax activations
        #self.out = tf.nn.softmax(fc8)
        return self.out

3.2 测试代码

定义好网络结构之后,我们需要进行测试:这里在读取数据时我采用两种方式,代码中会有解释。train.py代码:

# -*- coding: utf-8 -*-
"""
Created on Wed Apr 11 16:42:09 2018
@author: Administrator
"""
'''
用于训练AlexNet网络
由于内存有限,不能一次读取所有的数据,因此采用每次读取小批量的图片
1.在datagenerator.py文件中 把所有图片保存成bmp格式文件
2.存储一个dict字典 每个元素为 (图片的相对路径,标签) 将这个字典序列化保存到文件
3.读取该文件,把文件名顺序打乱,分成多组,每次加载mini_batch个图片进行训练
4.在加载图片时,需要把图片放大为227*227的 这是由于AlexNet网络输入的维度
'''

import  tensorflow as tf
from    alexnet  import alexnet
import    datagenerator 
import numpy as np
import cv2
import random
import network
import os


def remove_model_file(file):
    '''
    删除保存的模型文件
    删除成功返回True 否则返回 False
    
    args:
        file:模型文件名
    '''    
    path = [file+item for item in ('.index','.meta')]
    path.append(os.path.join( os.path.dirname(path[0]),'checkpoint'))
    for f in path:
        if os.path.isfile(f):
            os.remove(f)            
    ret = True
    #检查文件是否存在
    for f in path:
        if os.path.isfile(f):
            ret = False
    return ret
def alexnet_main(): ''' 使用AlexNet测试 由于数据量比较大,图片不能一次全部加载进来,因此采用分批读取图片数据 由于这里每次从硬盘读取batch_size大小的图片,速度较慢 ''' ''' 一 加载数据 ''' training_data,test_data = datagenerator.load_data() ''' 二 定义网络参数 ''' model_name = './alexnet_param/alexnet_model.ckpt' #模型保存所在文件 #定义网络超参数 learning_rate = 1e-4 #学习率 training_epoches = 40 #训练轮数 batch_size = 256 #小批量大小 #定义网络参数 n_classes = 10 #标签的维度 pdropout = 0.5 #弃权的概率 n_train = len(training_data) #训练集数据长度 n_test = len(test_data) #测试卷数据长度 image_size = (227,227) #图片大小 #如果目录不存在创建目录 if not os.path.isdir(os.path.dirname(model_name)): os.mkdir(os.path.dirname(model_name)) ''' 三 求解 构建模型 开始测试 ''' alex = alexnet(n_classes) #创建Saver op用于保存和恢复所有变量 saver = tf.train.Saver() #定义学习步骤 optimizer = tf.train.AdamOptimizer(learning_rate) train = optimizer.minimize(alex.cost) #train = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost) with tf.Session() as sess: sess.run(tf.global_variables_initializer()) #初始化变量 #如果该文件存在,恢复模型数据 if os.path.isfile(model_name+'.meta'): saver.restore(sess,model_name) count = 0 #开始迭代 for i in range(training_epoches): #打乱训练集 random.shuffle(training_data) #分组 mini_batchs = [training_data[k:k+batch_size] for k in range(0,n_train,batch_size)] #遍历每一个mini_batch for mini_batch in mini_batchs: print('输出',mini_batch[0]) x_batch,y_batch = datagenerator.get_image_data_and_label(mini_batch,image_size=image_size,one_hot=True) #开始训练 train.run(feed_dict={alex.input_x:x_batch,alex.input_y:y_batch,alex.keep_prob:pdropout}) #每次训练一批后,输出训练集准确率 training_accuracy,training_cost = sess.run([alex.accuracy,alex.cost],feed_dict={alex.input_x:x_batch,alex.input_y:y_batch,alex.keep_prob:pdropout}) count +=1 print('Iteration {0}:Training set accuracy {1},cost {2}.'.format(count,np.mean(training_accuracy),np.mean(training_cost))) ''' 每一轮训练完成做测试 #输出测试集准确率 如果一次性全部做测试,内容不够用会出现OOM错误。所以测试时选取比较小的mini_batch来测试 ''' #分组 mini_batchs = [test_data[k:k+batch_size] for k in range(0,n_test,batch_size)] test_accuracy_sum = [] test_cost_sum = [] #遍历每一个mini_batch for mini_batch in mini_batchs: x_batch,y_batch = datagenerator.get_image_data_and_label(mini_batch,image_size=image_size,one_hot=True) #校验 test_accuracy,test_cost = sess.run([alex.accuracy,alex.cost],feed_dict={alex.input_x:x_batch,alex.input_y:y_batch,alex.keep_prob:pdropout}) test_accuracy_sum.append(test_accuracy) test_cost_sum.append(test_cost) #迭代完一轮测试集,求平均 print('Epoch {0}: Test set accuracy {1},cost {2}.'.format(i,np.mean(test_accuracy_sum),np.mean(test_cost_sum))) #tensorflow的saver,用于保存模型 if remove_model_file(model_name) == True: #删除原先模型文件 saver.save(sess,model_name) print('模型保存成功!') def alexnet_improve_main(): ''' 使用AlexNet测试 这里采用一次获取所有的图片数据和标签,然后每次训练时从这些数据中随机选取batch_size去训练 ''' ''' 一 加载数据 ''' data_dir = 'cifar-10-batches-py' #数据所在目录 filelist = [] for i in range(5): name = os.path.join(data_dir,str('data_batch_%d'%(i+1))) filelist.append(name) data = datagenerator.datagenerator() #获取训练集数据 train_x = data.get_data_by_keyword('data',filelist, normalized=True,size=(32,32)) #标签 train_y = data.get_data_by_keyword('labels',filelist) filelist = [os.path.join(data_dir,'test_batch')] #获取训练集数据 数据标准化为0-1之间 test_x = data.get_data_by_keyword('data',filelist, normalized=True,size=(32,32)) #标签 test_y = data.get_data_by_keyword('labels',filelist) ''' 二 定义网络参数 ''' tf.reset_default_graph() tf.flags.DEFINE_string('model_file_name','./alexnet_improve_param/alexnet_model.ckpt','model file name (default:./alexnet_improve_param/alexnet_model.ckpt) ') #定义网络超参数 tf.flags.DEFINE_float('learning_rate', 0.0001,'learning rate (default:0.0001)') tf.flags.DEFINE_float('pdropout' , 0.5,'Dropout keep probability (default:0.5)') #定义网络参数 tf.flags.DEFINE_integer('training_epoches',5,'Number of training epochs (default:20)') tf.flags.DEFINE_integer('batch_size', 512,'Batch size (default:256)') n_classes = 10 #标签的维度 n_train = len(train_y) #训练集数据长度 n_test = len(test_y) #测试卷数据长度 image_size = (227,227) #图片大小 FLAGS = tf.flags.FLAGS FLAGS._parse_flags() print('网络参数') for attr,value in sorted(FLAGS.__flags.items()): print('{0}:{1}'.format(attr.upper(),value)) #如果目录不存在创建目录 if not os.path.isdir(os.path.dirname(FLAGS.model_file_name)): os.mkdir(os.path.dirname(FLAGS.model_file_name)) ''' 三 求解 构建模型 开始测试 ''' #构建alexnet网络 alex = alexnet(n_classes) #创建Saver op用于保存和恢复所有变量 saver = tf.train.Saver() #定义学习步骤 optimizer = tf.train.AdamOptimizer(FLAGS.learning_rate) train = optimizer.minimize(alex.cost) #train = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost) #设置tensorflow对GPU使用按需分配 config = tf.ConfigProto() config.gpu_options.allow_growth = True with tf.Session(config=config) as sess: #初始化变量 sess.run(tf.global_variables_initializer()) #如果该文件存在,恢复模型数据 if os.path.isfile(FLAGS.model_file_name + '.meta'): saver.restore(sess, FLAGS.model_file_name) #开始迭代 for epoch in range(FLAGS.training_epoches): indices = np.arange(n_train) #打乱训练集 random.shuffle(indices) #这里取n_train-batch_size+1 是为了防止indices[i+j]溢出 for i in range(0, n_train - FLAGS.batch_size + 1, FLAGS.batch_size): x_batch = [] y_batch = [] for j in range(FLAGS.batch_size): x_batch.append(cv2.resize(train_x[indices[i+j]], image_size)/255) y_batch.append(train_y[indices[i+j]]) #转换为one_hot格式 y_batch = datagenerator.get_one_hot_label(y_batch,n_classes) #开始训练 train.run(feed_dict={alex.input_x:x_batch,alex.input_y:y_batch,alex.keep_prob:FLAGS.pdropout}) #training_accuracy,training_cost = sess.run([alex.accuracy,alex.cost],feed_dict={alex.input_x:x_batch,alex.input_y:y_batch,alex.keep_prob:FLAGS.pdropout}) #print('Iteration {0}:Training set accuracy {1},cost {2}.'.format(int((i+epoch * n_train)/FLAGS.batch_size) ,np.mean(training_accuracy),np.mean(training_cost))) #迭代完一轮后 输出训练集准确率 training_accuracy_sum = [] training_cost_sum = [] for i in range(0,n_train-FLAGS.batch_size+1,FLAGS.batch_size): x_batch = [] y_batch = [] for j in range(FLAGS.batch_size): x_batch.append(cv2.resize(train_x[i+j],image_size)/255) y_batch.append(train_y[i+j]) #转换为one_hot格式 y_batch = datagenerator.get_one_hot_label(y_batch,n_classes) training_accuracy,training_cost = sess.run([alex.accuracy,alex.cost],feed_dict={alex.input_x:x_batch,alex.input_y:y_batch,alex.keep_prob:FLAGS.pdropout}) training_accuracy_sum.append(training_accuracy) training_cost_sum.append(training_cost) print('Epoch {0}:Training set accuracy {1},cost {2}.'.format(epoch,np.mean(training_accuracy_sum),np.mean(training_cost_sum))) ''' 每一轮训练完成做测试 #输出测试集准确率 如果一次性全部做测试,内容不够用会出现OOM错误。所以测试时选取比较小的mini_batch来测试 ''' test_accuracy_sum = [] test_cost_sum = [] for i in range(0,n_test-FLAGS.batch_size+1,FLAGS.batch_size): x_batch = [] y_batch = [] for j in range(FLAGS.batch_size): x_batch.append(cv2.resize(test_x[i+j],image_size)/255) y_batch.append(test_y[i+j]) #转换为one_hot格式 y_batch = datagenerator.get_one_hot_label(y_batch,n_classes) #校验 test_accuracy,test_cost = sess.run([alex.accuracy,alex.cost],feed_dict={alex.input_x:x_batch,alex.input_y:y_batch,alex.keep_prob:FLAGS.pdropout}) test_accuracy_sum.append(test_accuracy) test_cost_sum.append(test_cost) #迭代完一轮测试集,求平均 print('Epoch {0}: Test set accuracy {1},cost {2}.'.format(epoch,np.mean(test_accuracy_sum),np.mean(test_cost_sum))) #tensorflow的saver,用于保存模型 if remove_model_file(FLAGS.model_file_name) == True: #删除原先模型文件 saver.save(sess,FLAGS.model_file_name) print('模型保存成功!') ''' 测试 ''' if __name__ == '__main__': #alexnet_main() alexnet_improve_main()

由于网络比较大训练时间比较长,我只训练了5次,并设置每次训练的batch_size=512。运行结果如下:

然后我们可以在之前训练的基础上继续训练。后面我又稍微对程序进行了改进,加入了检查点的保存,每迭代一轮保存一次参数,并在输出的时候打印出时间,然后运行几轮后输出如下:

我们可以执行以下程序,查看我们保存的模型数据:然后截取部分内容显示:

from tensorflow.python.tools.inspect_checkpoint import print_tensors_in_checkpoint_file
print_tensors_in_checkpoint_file('./alexnet_improve_param/alexnet_model.ckpt',None,True)

 我们以输出层为例,为什么输出的张量名字为out/out/b和out/w,这是因为我们在定义Alexnet网络的时候定义了一个函数:

def fc(x, num_out,name, relu=True):
    '''
    全连接网络
    '''
    num_in = int(x.get_shape()[-1])
    with tf.name_scope(name) as scope:

        # Create tf variables for the weights and biases
        weights = tf.get_variable(scope+'w', shape=[num_in, num_out],
                                             dtype = tf.float32,
                                 initializer = tf.contrib.layers.xavier_initializer_conv2d())
        '''
        或者写成
        shape=[num_in, num_out]
        weights = tf.get_variable(scope+'w', shape=shape,dtype = tf.float32)
        
        '''
        
        biases = tf.Variable(tf.constant(0.0,shape=[num_out]),dtype=tf.float32,trainable=True,name=scope+'b')                                 


        if relu:
            act = tf.nn.xw_plus_b(x, weights, biases)
            # Apply ReLu non linearity
            relu = tf.nn.relu(act, name=scope)
            return relu
        else:
            act = tf.nn.xw_plus_b(x, weights, biases, name=scope)
            return act
    

并且输出层定义如下:

当给fc()函数传入name为'out'时候,get_variable()函数首先去查找是否定义了out/w(scope输出的是字符串为out/)张量,如果没有会创建一个,所以在保存的模型里有名称为out/w的张量。对于偏置我们使用的是Variable()函数,这个函数会始终创建新的变量,如果名称重复会自行处理,并且默认会在变量名称前加上name_scope()中定义的名称作为前缀。所以biases的名称就变成了out/out/b,因此如果我们想保存的张量名字是out/b的话,我们应该把fc()函数中biases这一行改成:

 biases = tf.Variable(tf.constant(0.0,shape=[num_out]),dtype=tf.float32,trainable=True,name='b')                                 
posted @ 2018-04-14 21:54  大奥特曼打小怪兽  阅读(8180)  评论(0编辑  收藏  举报
如果有任何技术小问题,欢迎大家交流沟通,共同进步