当前位置:   article > 正文

kaggle猫狗大战之AlexNet(一)_猫狗大战——alexnet模型

猫狗大战——alexnet模型

这篇文章主要介绍如何利用AlexNet预训练模型来训练一个猫狗分类器,主要内容包括:

  • 项目结构介绍
  • 数据探索
  • 数据的准备
  • AlexNet模型的构建
  • 模型的训练和性能评估
  • 结果的提交

一、项目结构介绍

1、相关数据下载地址

项目地址:https://github.com/steelOneself/kaggle/tree/master/cat_vs_dog/AlexNet

数据下载地址:https://www.kaggle.com/c/dogs-vs-cats-redux-kernels-edition/data

AlexNet预训练文件下载地址:http://www.cs.toronto.edu/~guerzhoy/tf_alexnet/

2、项目文件介绍

checkpoints  --- 用来保存训练后生成的模型文件

model --- 用来存放预训练模型文件

tensorboard ---用来保存训练过程中的日志信息

txt --- 用来存放数据集的图片信息

AlexNet.py --- AlexNet模型结构文件

DataGenrator.py --- 数据生成类

Exploration.py --- 可视化分析

Generate_txt.py --- 将图片信息保存为txt文件

run.py --- 模型训练、评估、结构生成

util_data.py --- 数据集工具类,将数据分为训练集和验证集

二、数据探索

1、数据介绍

数据包含两部分,训练集和测试集,训练集有25000张图片,测试集有12500张图片,在训练集的图片名称中包含了图片的标签信息,而测试集的图片名称代表的图片的id,提交结果的时候要求提交图片的id和预测对应图片的标签(1表示dog,0表示cat)。

2、数据探索

分析训练集中猫狗的分布情况

三、数据的准备

注意:我将展示出来的代码进行了省略,由于代码比较多,比较占空间,详细代码请参考git。

1、生成txt文件

  1. import os
  2. #设置txt保存目录
  3. save_txt_dir = "txt"
  4. def generate_txt(save_mode,train_img_dir):
  5. '''将图片的id和标签信息写入到txt中
  6. :param save_mode: train or test
  7. :param train_img_dir: 图片所在的目录
  8. :return: 空
  9. '''
  10. .....
  11. if __name__ == "__main__":
  12. #将训练集图片保存为txt文件
  13. generate_txt("train","D:/dataset/kaggle/cat_or_dog/train/train")
  14. #将测试集图片保存为txt文件
  15. generate_txt("test","D:/dataset/kaggle/cat_or_dog/test/test")

将图片信息保存为txt文件,保存格式:图片id,图片路径,图片标签

2、将数据分为训练集和验证集

  1. import pandas as pd
  2. from sklearn.utils import shuffle
  3. def get_img_infos(mode,img_info_txt,label_name_to_num=None):
  4. '''读取txt中存储的图片信息
  5. :param mode: train or test
  6. :param img_info_txt: 文件信息存储的txt路径
  7. :param label_name_to_num: 将字符串标签转为数字
  8. :return: 图片id信息和图片的标签(mode为train时不为空,mode为test时为空)
  9. '''
  10. ...
  11. return img_ids,img_labels,img_paths
  12. def split_dataset(img_ids,img_paths,img_labels,val_size=0.1):
  13. '''
  14. :param img_ids: 图片的id列表
  15. :param img_paths: 图片的路径列表
  16. :param img_labels: 图片的标签列表
  17. :param val_size: 验证集大小5000
  18. :param test_size: 测试集大小10000
  19. :return: 训练集数据,验证集数据,测试集数据
  20. '''
  21. ...
  22. #将训练集文件和测试集文件保存为csv文件
  23. train_dataset.to_csv("txt/train.csv")
  24. val_dataset.to_csv("txt/val.csv")
  25. return train_dataset,val_dataset
  26. if __name__ == "__main__":
  27. train_img_ids,train_img_labels,img_paths = get_img_infos("train","txt/train.txt")
  28. train_dataset,val_dataset = split_dataset(train_img_ids,img_paths,train_img_labels)

将25000张图片分为训练集和验证集,训练集占20000张图片,验证集占5000张图片,分割的时候需要注意,训练集和验证集中猫和狗所占的比例相同。

3、数据生成类

  1. import tensorflow as tf
  2. import numpy as np
  3. from tensorflow.python.framework.ops import convert_to_tensor
  4. class ImageDataGenerator(object):
  5. '''
  6. 初始化图片生成参数
  7. '''
  8. ......
  9. def _parse_function_train(self,filename,label):
  10. #将标签转为one-hot编码
  11. one_hot = tf.one_hot(label,self.num_classes)
  12. #加载图片的预处理
  13. img_string = tf.read_file(filename)
  14. img_decode = tf.image.decode_jpeg(img_string,channels=3)
  15. img_resized = tf.image.resize_images(img_decode,[227,227])
  16. return img_resized,one_hot

利用CPU资源来加载数据,在读取图片的时候需要将图片转为227×227,因为AlexNet要求输出图片的大小是227×227。

四、AlexNet模型构建

  1. import tensorflow as tf
  2. import numpy as np
  3. '''
  4. 卷积函数
  5. '''
  6. def conv(x,filter_height,filter_width,num_filters,stride_y,stride_x,name,padding="SAME",groups=1):
  7. #获取输入tensor的channel
  8. input_channels = int(x.get_shape()[-1])
  9. #创建一个lambda函数
  10. convolve = lambda i,k:tf.nn.conv2d(i,k,strides=[1,stride_y,stride_x,1],padding=padding)
  11. with tf.variable_scope(name) as scope:
  12. #定义权重
  13. weights = tf.get_variable("weights",shape=[filter_height,filter_width,
  14. input_channels/groups,num_filters])
  15. #定义偏置
  16. biases = tf.get_variable("biases",shape=[num_filters])
  17. if groups == 1:
  18. conv = convolve(x,weights)
  19. else:
  20. input_groups = tf.split(axis=3,num_or_size_splits=groups,value=x)
  21. weight_groups = tf.split(axis=3,num_or_size_splits=groups,value=weights)
  22. output_groups = [convolve(i,k) for i,k in zip(input_groups,weight_groups)]
  23. #连接卷积层
  24. conv = tf.concat(axis=3,values=output_groups)
  25. bias = tf.reshape(tf.nn.bias_add(conv,biases),tf.shape(conv))
  26. #relu激活函数
  27. relu = tf.nn.relu(bias,name=scope.name)
  28. return relu
  29. '''
  30. 全连接层函数
  31. '''
  32. def fc(x,num_in,num_out,name,relu=True):
  33. with tf.variable_scope(name) as scope:
  34. #定义权重和偏置
  35. weights = tf.get_variable("weights",shape=[num_in,num_out],trainable=True)
  36. biases = tf.get_variable("biases",[num_out],trainable=True)
  37. fc_out = tf.nn.xw_plus_b(x,weights,biases,name=scope.name)
  38. if relu:
  39. fc_out = tf.nn.relu(fc_out)
  40. return fc_out
  41. '''
  42. 最大池化层函数
  43. '''
  44. def max_pool(x,filter_height,filter_width,stride_y,stride_x,name,padding="SAME"):
  45. return tf.nn.max_pool(x,ksize=[1,filter_height,filter_width,1],strides=[1,stride_y,stride_x,1],
  46. padding=padding,name=name)
  47. '''
  48. lrn层
  49. '''
  50. def lrn(x,radius,alpha,beta,name,bias=1.0):
  51. return tf.nn.local_response_normalization(x,depth_radius=radius,alpha=alpha,beta=beta,bias=bias,name=name)
  52. '''
  53. dropout层
  54. '''
  55. def dropout(x,keep_prob):
  56. return tf.nn.dropout(x,keep_prob)
  57. '''
  58. 定义AlexNet类
  59. '''
  60. class AlexNet(object):
  61. '''
  62. 初始化AlexNet网络
  63. 参数:
  64. x:输入的tensor
  65. keep_prob:dropout节点保留概率
  66. num_classes:需要分类的数量
  67. skip_layer:需要重新训练的层
  68. weights_path:预训练参数文件的路径
  69. '''
  70. def __init__(self,x,keep_prob,num_classes,skip_layer,weights_path="default"):
  71. self.X = x
  72. self.KEEP_PROB = keep_prob
  73. self.NUM_CLASSES = num_classes
  74. self.SKIP_LAYER = skip_layer
  75. if weights_path == "default":
  76. self.WEIGHTS_PATH = "model/bvlc_alexnet.npy"
  77. else:
  78. self.WEIGHTS_PATH = weights_path
  79. self.create()
  80. '''
  81. 创建AlexNet网络的计算图
  82. '''
  83. def create(self):
  84. #第一层卷积
  85. conv1 = conv(self.X,11,11,96,4,4,padding="VALID",name="conv1")
  86. norm1 = lrn(conv1,2,2e-05,0.75,name="norm1")
  87. pool1 = max_pool(norm1,3,3,2,2,padding="VALID",name="pool1")
  88. #第二层卷积
  89. conv2 = conv(pool1,5,5,256,1,1,groups=2,name="conv2")
  90. norm2 = lrn(conv2,2,2e-05,0.75,name="norm2")
  91. pool2 = max_pool(norm2,3,3,2,2,padding="VALID",name="pool2")
  92. #第三层卷积
  93. conv3 = conv(pool2,3,3,384,1,1,name="conv3")
  94. #第四层卷积
  95. conv4 = conv(conv3,3,3,384,1,1,groups=2,name="conv4")
  96. #第五层卷积
  97. conv5 = conv(conv4,3,3,256,1,1,groups=2,name="conv5")
  98. pool5 = max_pool(conv5,3,3,2,2,padding="VALID",name="pool5")
  99. #第六层,全连接层
  100. flattened = tf.reshape(pool5,[-1,6*6*256])
  101. fc6 = fc(flattened,6*6*256,4096,name="fc6")
  102. dropout6 = dropout(fc6,self.KEEP_PROB)
  103. #第七层,全连接层
  104. fc7 = fc(dropout6,4096,4096,name="fc7")
  105. dropout7 = dropout(fc7,self.KEEP_PROB)
  106. #第八层,全连接层
  107. self.fc8 = fc(dropout7,4096,self.NUM_CLASSES,relu=False,name="fc8")
  108. '''
  109. 加载预训练权重文件初始化权重
  110. '''
  111. def load_initial_weights(self,session):
  112. #加载预训练权重文件
  113. weights_dict = np.load(self.WEIGHTS_PATH,encoding="bytes").item()
  114. #遍历所有的层,看是否需要重新训练
  115. for op_name in weights_dict:
  116. if op_name not in self.SKIP_LAYER:
  117. with tf.variable_scope(op_name,reuse=True):
  118. for data in weights_dict[op_name]:
  119. if len(data.shape) == 1:
  120. var = tf.get_variable("biases",trainable=False)
  121. session.run(var.assign(data))
  122. else:
  123. var = tf.get_variable("weights",trainable=False)
  124. session.run(var.assign(data))

五、模型的训练和性能评估

1、模型的训练

模型参数设置

  1. #设置训练文件的路径
  2. train_txt = "txt/train.txt"
  3. test_txt = "txt/test.txt"
  4. learning_rate = 0.0001
  5. num_epochs = 10
  6. batch_size = 128
  7. dropout_rate = 0.5
  8. num_classes = 2
  9. train_layers = ["fc6","fc7","fc8"]

train_layers设置需要重新训练的层数,在这次训练过程中,只重新训练AlexNet的最后三层全连接层,其余的层保持不变。

训练完成之后,在checkpoints会产生ckpt模型文件,每一个epoch保存一次模型文件,只有当后一个在验证集上的准确率大于前一个时才会保存模型文件,在保存模型文件的时候后面有附带该次epoch在验证集上的准确率。

2、模型评估

3、查看模型在验证集上分类正确和分类错误的图片

上面一行表示分类正确的图片,下面一行表示分类错误的图片

4、查看验证集预测结果的分布情况

5、混淆矩阵

6、验证集分类结果报告

六、提交结果

kaggle的成绩是计算预测结果的交叉熵损失值,在生成预测结果的时候,通过fc8输出的结果还需要经过一个softmax层才能输出每个类别的概率,直接使用预测类标(0或1)比预测概率的成绩会低一些。

总结:本篇文章主要介绍了如何使用AlexNet来构建一个预训练模型,在下一篇文章将会介绍如何来预训练一个更复杂的Inception-resent网络。

 

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号