精华内容
下载资源
问答
  • 完整实现利用tensorflow训练自己图片数据集

    万次阅读 多人点赞 2018-07-22 17:04:21
    经过差不多一个礼拜的时间的学习,终于把完整的一个利用自己爬取的图片做训练数据集的卷积神经网络的实现(基于tensorflow) 目录 总体思路 第三部分:预处理 第四部分:网络模型 第五部分:训练 2019.4.22...

    经过差不多一个礼拜的时间的学习,终于把完整的一个利用自己爬取的图片做训练数据集的卷积神经网络的实现(基于tensorflow)

     

    目录

    总体思路

    第三部分:预处理

    第四部分:网络模型

    第五部分:训练

    2019.4.22更新

    第六部分:测试

    特别说明


     

     

    2019.1补充说明,针对评论区的几个问题做了一些改进和更新

    1、训练集和测试集的划分问题

    关于训练集和测试集的补充说明:

    训练集和测试集的划分很重要,我自己尝试过两种简单的方法:

    1)训练集和验证集在预处理的时候按比例随机划分。在PreWork.py添加了这部分代码

    2)一开始训练集和测试集就提前划分好存储在不同的.list文件里。

    读训练数据集就从train_list_path下读,读测试样本就从test_list_path下读

    train_list_path = './list/train.list'  # 训练样本的读入路径
    # test_list_path = './list/test.list'  # 测试样本的读入路径

    在实际应用中数据量不一定足够,可以采用交叉验证的方法划分训练集、验证集、测试集。这部分内容不详细展开,简单做个记录。

    • 留出法。将原样本随机划分为训练集和验证集(本博文使用的方法)。缺点:具有随机性,最后评估指标与原始分组有较大关系。
    • k折交叉验证。样本分为k个大小相等的样本子集,k-1个用来训练,1个用来测试,最后k次评估指标的平均值作为最终评估指标。
    • 留一法。每次留一个样本作为验证(是K折交叉验证的特殊情况),适用于数据缺乏的情况下。
    • 自助法。基于自助采用,对数据集随机采样,重复m次有得到m个子集,由于有部分样本没有被抽到过,有的重复多次抽到,那么将从未被抽到的作为验证集。

    2、测试代码的补充


     

    总体思路

    简单整理一下思路

    1. 获取数据集(上网爬取,或者直接找公开的图片数据集)
    2. 数据集的预处理,包括reshape、去噪、对比度增强、批量剪切、图片灰度、二值化、缩放、丰富数据等等(公开数据集一般已处理完)
    3. 对图片集进行处理,得到网络的输入(2、3都可以看成预处理部分)
    4. 搭建卷积神经网络框架
    5. 进行网络的训练
    6. 读取训练好的网络模型完成测试

    每三、四部分的参考和具体实现都在前面的两篇博客里提到了

    搭建一个卷积神经网络

    利用tensorflow训练自己的图片数据集——数据准备

    再附一下重点参考:江湖人称星爷的博客。给了我很大的指引和参考。现在附的跟前面有一点改动,基本思路是一样的。


    第一、二部分还有一些小问题,暂时先不放上来。

     

    第三部分:预处理

    新建PreWork.py文件

    '''
    PreWork.py
    功能:实现对指定大小的生成图片进行sample与label分类制作
    获得神经网络输入的get_files文件,同时为了方便网络的训练,输入数据进行batch处理。
    
    2018/7/19完成
    -------copyright@GCN-------
    '''
    
    import os
    import numpy as np
    from PIL import Image
    import tensorflow as tf
    import matplotlib.pyplot as plt
    from numpy import *
    
    angry = []
    label_angry = []
    disgusted = []
    label_disgusted = []
    fearful = []
    label_fearful = []
    happy = []
    label_happy = []
    sadness = []
    label_sadness = []
    surprised = []
    label_surprised = []
    
    
    def get_file(file_dir):
        # step1:获取路径下所有的图片路径名,存放到
        # 对应的列表中,同时贴上标签,存放到label列表中。
        for file in os.listdir(file_dir + '/angry'):
            angry.append(file_dir + '/angry' + '/' + file)
            label_angry.append(0)
        for file in os.listdir(file_dir + '/disgusted'):
            disgusted.append(file_dir + '/disgusted' + '/' + file)
            label_disgusted.append(1)
        for file in os.listdir(file_dir + '/fearful'):
            fearful.append(file_dir + '/fearful' + '/' + file)
            label_fearful.append(2)
        for file in os.listdir(file_dir + '/happy'):
            happy.append(file_dir + '/happy' + '/' + file)
            label_happy.append(3)
        for file in os.listdir(file_dir + '/sadness'):
            sadness.append(file_dir + '/sadness' + '/' + file)
            label_sadness.append(4)
        for file in os.listdir(file_dir + '/surprised'):
            surprised.append(file_dir + '/surprised' + '/' + file)
            label_surprised.append(5)
    
        # 打印出提取图片的情况,检测是否正确提取
        print("There are %d angry\nThere are %d disgusted\nThere are %d fearful\n" %(len(angry), len(disgusted), len(fearful)),end="")
        print("There are %d happy\nThere are %d sadness\nThere are %d surprised\n" %(len(happy),len(sadness),len(surprised)))
    
        # step2:对生成的图片路径和标签List做打乱处理把所有的合起来组成一个list(img和lab)
        # 合并数据numpy.hstack(tup)
        # tup可以是python中的元组(tuple)、列表(list),或者numpy中数组(array),函数作用是将tup在水平方向上(按列顺序)合并
        image_list = np.hstack((angry, disgusted, fearful, happy, sadness, surprised))
        label_list = np.hstack((label_angry, label_disgusted, label_fearful, label_happy, label_sadness, label_surprised))
        # 利用shuffle,转置、随机打乱
        temp = np.array([image_list, label_list])   # 转换成2维矩阵
        temp = temp.transpose()     # 转置
        # numpy.transpose(a, axes=None) 作用:将输入的array转置,并返回转置后的array
        np.random.shuffle(temp)     # 按行随机打乱顺序函数
    
        # 将所有的img和lab转换成list
        all_image_list = list(temp[:, 0])    # 取出第0列数据,即图片路径
        all_label_list = list(temp[:, 1])    # 取出第1列数据,即图片标签
        label_list = [int(i) for i in label_list]   # 转换成int数据类型
        
        ''' 
        # 将所得List分为两部分,一部分用来训练tra,一部分用来验证val
        n_sample = len(all_label_list)
        n_val = int(math.ceil(n_sample * ratio))  # 验证样本数, ratio是验证集的比例
        n_train = n_sample - n_val  # 训练样本数
    
        tra_images = all_image_list[0:n_train]
        tra_labels = all_label_list[0:n_train]
        tra_labels = [int(float(i)) for i in tra_labels]   # 转换成int数据类型
        val_images = all_image_list[n_train:-1]
        val_labels = all_label_list[n_train:-1]
        val_labels = [int(float(i)) for i in val_labels]   # 转换成int数据类型
    
        return tra_images, tra_labels, val_images, val_labels
        '''
    
        return image_list, label_list
    
    # 将image和label转为list格式数据,因为后边用到的的一些tensorflow函数接收的是list格式数据
    # 为了方便网络的训练,输入数据进行batch处理
    # image_W, image_H, :图像高度和宽度
    # batch_size:每个batch要放多少张图片
    # capacity:一个队列最大多少
    def get_batch(image, label, image_W, image_H, batch_size, capacity):
        # step1:将上面生成的List传入get_batch() ,转换类型,产生一个输入队列queue
        # tf.cast()用来做类型转换
        image = tf.cast(image, tf.string)   # 可变长度的字节数组.每一个张量元素都是一个字节数组
        label = tf.cast(label, tf.int32)
        # tf.train.slice_input_producer是一个tensor生成器
        # 作用是按照设定,每次从一个tensor列表中按顺序或者随机抽取出一个tensor放入文件名队列。
        input_queue = tf.train.slice_input_producer([image, label])
        label = input_queue[1]
        image_contents = tf.read_file(input_queue[0])   # tf.read_file()从队列中读取图像
    
        # step2:将图像解码,使用相同类型的图像
        image = tf.image.decode_jpeg(image_contents, channels=3)
        # jpeg或者jpg格式都用decode_jpeg函数,其他格式可以去查看官方文档
    
        # step3:数据预处理,对图像进行旋转、缩放、裁剪、归一化等操作,让计算出的模型更健壮。
        image = tf.image.resize_image_with_crop_or_pad(image, image_W, image_H)
        # 对resize后的图片进行标准化处理
        image = tf.image.per_image_standardization(image)
    
        # step4:生成batch
        # image_batch: 4D tensor [batch_size, width, height, 3], dtype = tf.float32
        # label_batch: 1D tensor [batch_size], dtype = tf.int32
        image_batch, label_batch = tf.train.batch([image, label], batch_size=batch_size, num_threads=16, capacity=capacity)
    
        # 重新排列label,行数为[batch_size]
        label_batch = tf.reshape(label_batch, [batch_size])
        # image_batch = tf.cast(image_batch, tf.uint8)    # 显示彩色图像
        image_batch = tf.cast(image_batch, tf.float32)    # 显示灰度图
        # print(label_batch) Tensor("Reshape:0", shape=(6,), dtype=int32)
        return image_batch, label_batch
        # 获取两个batch,两个batch即为传入神经网络的数据
    
    """
    def PreWork():
        # 对预处理的数据进行可视化,查看预处理的效果
        IMG_W = 256
        IMG_H = 256
        BATCH_SIZE = 6
        CAPACITY = 64
    
        train_dir = 'F:/Python/PycharmProjects/DeepLearning/CK+_part'
    
        # image_list, label_list, val_images, val_labels = get_file(train_dir)
        image_list, label_list = get_file(train_dir)
        image_batch, label_batch = get_batch(image_list, label_list, IMG_W, IMG_H, BATCH_SIZE, CAPACITY)
        print(label_batch.shape)
    
        lists = ('angry', 'disgusted', 'fearful', 'happy', 'sadness', 'surprised')
    
        with tf.Session() as sess:
            i = 0
            coord = tf.train.Coordinator()  # 创建一个线程协调器,用来管理之后在Session中启动的所有线程
            threads = tf.train.start_queue_runners(coord=coord)
            try:
                while not coord.should_stop() and i < 1:
                    # 提取出两个batch的图片并可视化。
                    img, label = sess.run([image_batch, label_batch])  # 在会话中取出img和label
                    # img = tf.cast(img, tf.uint8)
    
                    '''
                    1、range()返回的是range object,而np.arange()返回的是numpy.ndarray()
                    range(start, end, step),返回一个list对象,起始值为start,终止值为end,但不含终止值,步长为step。只能创建int型list。
                    arange(start, end, step),与range()类似,但是返回一个array对象。需要引入import numpy as np,并且arange可以使用float型数据。
                    
                    2、range()不支持步长为小数,np.arange()支持步长为小数
                    
                    3、两者都可用于迭代
                    range尽可用于迭代,而np.nrange作用远不止于此,它是一个序列,可被当做向量使用。
                    '''
                    for j in np.arange(BATCH_SIZE):
                        # np.arange()函数返回一个有终点和起点的固定步长的排列
                        print('label: %d' % label[j])
                        plt.imshow(img[j, :, :, :])
                        title = lists[int(label[j])]
                        plt.title(title)
                        plt.show()
                    i += 1
            except tf.errors.OutOfRangeError:
                print('done!')
            finally:
                coord.request_stop()
            coord.join(threads)
    
    if __name__ == '__main__':
        PreWork()
    """

     

    第四部分:网络模型

    新建CNNModel.py文件

    补充的知识点

    在TensorFlow的世界里,变量的定义和初始化是分开的,所有关于图变量的赋值和计算都要通过tf.Session的run来进行。
    
    想要将所有图变量进行集体初始化时应该使用tf.global_variables_initializer
    tf.placehold与tf.Variable的区别:
        tf.placehold 占位符
            主要为真实输入数据和输出标签的输入, 用于在 feed_dict中的变量,不需要指定初始值,具体值在feed_dict中的变量给出。
        tf.Variable 主要用于定义weights bias等可训练会改变的变量,必须指定初始值。
            通过Variable()构造函数后,此variable的类型和形状固定不能修改了,但值可以用assign方法修改。
    
    tf.get_variable和tf.Variable函数差别
    相同点:通过两函数创建变量的过程基本一样,
            tf.variable函数调用时提供的维度(shape)信息以及初始化方法(initializer)的参数和tf.Variable函数调用时提供的初始化过程中的参数基本类似。
    不同点:两函数指定变量名称的参数不同,
            对于tf.Variable函数,变量名称是一个可选的参数,通过name="v"的形式给出
            tf.get_variable函数,变量名称是一个必填的参数,它会根据变量名称去创建或者获取变量
    '''
    CNNModel.py
    含3层卷积池化层,2层全连接层,激活函数ReLU,采用dropout和softmax函数做分类器
    2018/7/18完成搭建,2018/7/19实现文件调用,2018/7/22修改网络结构
    -------copyright@GCN-------
    '''
    import tensorflow as tf
    
    
    # 函数申明
    def weight_variable(shape, n):
        # tf.truncated_normal(shape, mean, stddev)这个函数产生正态分布,均值和标准差自己设定。
        # shape表示生成张量的维度,mean是均值
        # stddev是标准差,,默认最大为1,最小为-1,均值为0
        initial = tf.truncated_normal(shape, stddev=n, dtype=tf.float32)
        return initial
    
    def bias_variable(shape):
        # 创建一个结构为shape矩阵也可以说是数组shape声明其行列,初始化所有值为0.1
        initial = tf.constant(0.1, shape=shape, dtype=tf.float32)
        return initial
    
    def conv2d(x, W):
        # 卷积遍历各方向步数为1,SAME:边缘外自动补0,遍历相乘
        # padding 一般只有两个值
        # 卷积层后输出图像大小为:(W+2P-f)/stride+1并向下取整
        return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
    
    def max_pool_2x2(x, name):
        # 池化卷积结果(conv2d)池化层采用kernel大小为3*3,步数也为2,SAME:周围补0,取最大值。数据量缩小了4倍
        # x 是 CNN 第一步卷积的输出量,其shape必须为[batch, height, weight, channels];
        # ksize 是池化窗口的大小, shape为[batch, height, weight, channels]
        # stride 步长,一般是[1,stride, stride,1]
        # 池化层输出图像的大小为(W-f)/stride+1,向上取整
        return tf.nn.max_pool(x, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='SAME', name=name)
    
    
    # 一个简单的卷积神经网络,卷积+池化层 x2,全连接层x2,最后一个softmax层做分类。
    # 64个3x3的卷积核(3通道),padding=’SAME’,表示padding后卷积的图与原图尺寸一致,激活函数relu()
    def deep_CNN(images, batch_size, n_classes):
        # 搭建网络
        # 第一层卷积
        # 第一二参数值得卷积核尺寸大小,即patch;第三个参数是通道数;第四个是卷积核个数
        with tf.variable_scope('conv1') as scope:
            # 所谓名字的scope,指当绑定了一个名字到一个对象的时候,该名字在程序文本中的可见范围
            w_conv1 = tf.Variable(weight_variable([3, 3, 3, 64], 1.0), name='weights', dtype=tf.float32)
            b_conv1 = tf.Variable(bias_variable([64]), name='biases', dtype=tf.float32)   # 64个偏置值
            # tf.nn.bias_add 是 tf.add 的一个特例:tf.add(tf.matmul(x, w), b) == tf.matmul(x, w) + b
            # h_conv1 = tf.nn.relu(tf.nn.bias_add(conv2d(images, w_conv1), b_conv1), name=scope.name)
            h_conv1 = tf.nn.relu(conv2d(images, w_conv1)+b_conv1, name='conv1')  # 得到128*128*64(假设原始图像是128*128)
        # 第一层池化
        # 3x3最大池化,步长strides为2,池化后执行lrn()操作,局部响应归一化,增强了模型的泛化能力。
        # tf.nn.lrn(input,depth_radius=None,bias=None,alpha=None,beta=None,name=None)
        with tf.variable_scope('pooling1_lrn') as scope:
            pool1 = max_pool_2x2(h_conv1, 'pooling1')   # 得到64*64*64
            norm1 = tf.nn.lrn(pool1, depth_radius=4, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name='norm1')
    
        # 第二层卷积
        # 32个3x3的卷积核(16通道),padding=’SAME’,表示padding后卷积的图与原图尺寸一致,激活函数relu()
        with tf.variable_scope('conv2') as scope:
            w_conv2 = tf.Variable(weight_variable([3, 3, 64, 32], 0.1), name='weights', dtype=tf.float32)
            b_conv2 = tf.Variable(bias_variable([32]), name='biases', dtype=tf.float32)   # 32个偏置值
            h_conv2 = tf.nn.relu(conv2d(norm1, w_conv2)+b_conv2, name='conv2')  # 得到64*64*32
    
        # 第二层池化
        # 3x3最大池化,步长strides为2,池化后执行lrn()操作
        with tf.variable_scope('pooling2_lrn') as scope:
            pool2 = max_pool_2x2(h_conv2, 'pooling2')  # 得到32*32*32
            norm2 = tf.nn.lrn(pool2, depth_radius=4, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name='norm2')
    
        # 第三层卷积
        # 16个3x3的卷积核(16通道),padding=’SAME’,表示padding后卷积的图与原图尺寸一致,激活函数relu()
        with tf.variable_scope('conv3') as scope:
            w_conv3 = tf.Variable(weight_variable([3, 3, 32, 16], 0.1), name='weights', dtype=tf.float32)
            b_conv3 = tf.Variable(bias_variable([16]), name='biases', dtype=tf.float32)   # 16个偏置值
            h_conv3 = tf.nn.relu(conv2d(norm2, w_conv3)+b_conv3, name='conv3')  # 得到32*32*16
    
        # 第三层池化
        # 3x3最大池化,步长strides为2,池化后执行lrn()操作
        with tf.variable_scope('pooling3_lrn') as scope:
            pool3 = max_pool_2x2(h_conv3, 'pooling3')  # 得到16*16*16
            norm3 = tf.nn.lrn(pool3, depth_radius=4, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name='norm3')
    
        # 第四层全连接层
        # 128个神经元,将之前pool层的输出reshape成一行,激活函数relu()
        with tf.variable_scope('local3') as scope:
            reshape = tf.reshape(norm3, shape=[batch_size, -1])
            dim = reshape.get_shape()[1].value
            w_fc1 = tf.Variable(weight_variable([dim, 128], 0.005),  name='weights', dtype=tf.float32)
            b_fc1 = tf.Variable(bias_variable([128]), name='biases', dtype=tf.float32)
            h_fc1 = tf.nn.relu(tf.matmul(reshape, w_fc1) + b_fc1, name=scope.name)
    
        # 第五层全连接层
        # 128个神经元,激活函数relu()
        with tf.variable_scope('local4') as scope:
            w_fc2 = tf.Variable(weight_variable([128 ,128], 0.005),name='weights', dtype=tf.float32)
            b_fc2 = tf.Variable(bias_variable([128]), name='biases', dtype=tf.float32)
            h_fc2 = tf.nn.relu(tf.matmul(h_fc1, w_fc2) + b_fc1, name=scope.name)
    
    
        # 对卷积结果执行dropout操作
        # keep_prob = tf.placeholder(tf.float32)
        h_fc2_dropout = tf.nn.dropout(h_fc2, 0.5)
        # tf.nn.dropout(x, keep_prob, noise_shape=None, seed=None, name=None)
        # 第二个参数keep_prob: 设置神经元被选中的概率,在初始化时keep_prob是一个占位符
    
        # Softmax回归层
        # 将前面的FC层输出,做一个线性回归,计算出每一类的得分,在这里是6类,所以这个层输出的是六个得分。
        with tf.variable_scope('softmax_linear') as scope:
            weights = tf.Variable(weight_variable([128, n_classes], 0.005), name='softmax_linear', dtype=tf.float32)
            biases = tf.Variable(bias_variable([n_classes]), name='biases', dtype=tf.float32)
            softmax_linear = tf.add(tf.matmul(h_fc2_dropout, weights), biases, name='softmax_linear')
            # softmax_linear = tf.nn.softmax(tf.add(tf.matmul(h_fc2_dropout, weights), biases, name='softmax_linear'))
        return softmax_linear
        # 最后返回softmax层的输出
    
    
    # loss计算
    # 传入参数:logits,网络计算输出值。labels,真实值,0或者1
    # 返回参数:loss,损失值
    def losses(logits, labels):
        with tf.variable_scope('loss') as scope:
            cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=labels, name='xentropy_per_example')
            loss = tf.reduce_mean(cross_entropy, name='loss')
            tf.summary.scalar(scope.name + '/loss', loss)
        return loss
    
    # loss损失值优化
    # 输入参数:loss。learning_rate,学习速率。
    # 返回参数:train_op,训练op,这个参数要输入sess.run中让模型去训练。
    def trainning(loss, learning_rate):
        with tf.name_scope('optimizer'):
            optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
            global_step = tf.Variable(0, name='global_step', trainable=False)
            train_op = optimizer.minimize(loss, global_step=global_step)
        return train_op
    
    # 评价/准确率计算
    # 输入参数:logits,网络计算值。labels,标签,也就是真实值,在这里是0或者1。
    # 返回参数:accuracy,当前step的平均准确率,也就是在这些batch中多少张图片被正确分类了。
    def evaluation(logits, labels):
        with tf.variable_scope('accuracy') as scope:
            correct = tf.nn.in_top_k(logits, labels, 1)
            accuracy = tf.reduce_mean(tf.cast(correct, tf.float16))
            tf.summary.scalar(scope.name + '/accuracy', accuracy)
        return accuracy
    
    

     

    第五部分:训练

    新建Train.py文件

    这部分代码基本来自参考博文。

    '''
    Train.py
    对搭建好的网络进行训练,并保存训练参数,以便下次使用
    2018/7/19实现文件调用,2018/7/22实现最终网络的训练
    -------copyright@GCN-------
    '''
    # 导入文件
    import os
    import numpy as np
    import tensorflow as tf
    from PreWork import get_file, get_batch
    from CNNModel import deep_CNN, losses, trainning, evaluation
    
    # 变量声明
    N_CLASSES = 6
    IMG_W = 28  # resize图像,太大的话训练时间久
    IMG_H = 28
    BATCH_SIZE = 20     # 每个batch要放多少张图片
    CAPACITY = 200      # 一个队列最大多少
    MAX_STEP = 10000 
    learning_rate = 0.0001  # 一般小于0.0001
    
    # 获取批次batch
    train_dir = './CK+_part'  # 训练样本的读入路径
    logs_train_dir = './CK+_part'  #logs存储路径
    train, train_label = get_file(train_dir)
    # 训练数据及标签
    train_batch, train_label_batch = get_batch(train, train_label, IMG_W, IMG_H, BATCH_SIZE, CAPACITY)
    
    # 训练操作定义
    train_logits = deep_CNN(train_batch, BATCH_SIZE, N_CLASSES)
    train_loss = losses(train_logits, train_label_batch)
    train_op = trainning(train_loss, learning_rate)
    train_acc = evaluation(train_logits, train_label_batch)
    
    # 这个是log汇总记录
    summary_op = tf.summary.merge_all()
    
    # 产生一个会话
    sess = tf.Session()
    train_writer = tf.summary.FileWriter(logs_train_dir, sess.graph)
    # 产生一个saver来存储训练好的模型
    saver = tf.train.Saver()
    # 所有节点初始化
    sess.run(tf.global_variables_initializer())
    # 队列监控
    coord = tf.train.Coordinator() # 设置多线程协调器
    threads = tf.train.start_queue_runners(sess=sess, coord=coord)
    
    # 进行batch的训练
    try:
        # 执行MAX_STEP步的训练,一步一个batch
        for step in np.arange(MAX_STEP):
            if coord.should_stop():
                break
            # 启动以下操作节点,有个疑问,为什么train_logits在这里没有开启?
            _, tra_loss, tra_acc = sess.run([train_op, train_loss, train_acc])
    
            # 每隔50步打印一次当前的loss以及acc,同时记录log,写入writer
            if step % 100 == 0:
                print('Step %d, train loss = %.2f, train accuracy = %.2f%%' % (step, tra_loss, tra_acc * 100.0))
                summary_str = sess.run(summary_op)
                train_writer.add_summary(summary_str, step)
    
            # 保存最后一次网络参数
            checkpoint_path = os.path.join(logs_train_dir, 'thing.ckpt')
            saver.save(sess, checkpoint_path)
    
            '''
            # 每隔100步,保存一次训练好的模型
            if (step + 1) == MAX_STEP:
                checkpoint_path = os.path.join(logs_train_dir, 'thing.ckpt')
                saver.save(sess, checkpoint_path, global_step=step)
            '''
            
    
    except tf.errors.OutOfRangeError:
        print('Done training -- epoch limit reached')
    
    finally:
        coord.request_stop()
    coord.join(threads)
    sess.close()
    
    
    
    

     

    2019.4.22更新

    之前用的数据集(CK+表情数据库)有一点问题:由于数据集中既有灰度图也有彩色图,在程序中设置通道数的时候就没有统一成一样的,所以测试的时候总出错。

    为了方便,直接把图片都换成3通道的彩色图像,修改测试代码。(当然也可以在预处理阶段,把所有的图片转换为统一通道数)

    从网上爬取了三个分类数据集(guitar, house, rabbit),将所有图片借用之前提到的参考文献的代码reshape成相同大小,数据集很小比较简单,所以跑了2000次,简单训练生成网络模型参数文件。

     

    测试代码终于可以跑通了,不过因为我模拟训练的时候数据不平衡,训练的样本也很小,实际测试的时候偶尔效果并不好。真正做的时候要根据实际问题的需求改进,这里只是做个示范就没有考虑这么多,仅供参考

     

     

    第六部分:测试

    新建Test.py文件,修改完成

    在‘hijacklei’的提醒下加了一行初始化代码

    '''
    Test.py
    2019/4/22完成最终网络的测试
    -------copyright@GCN-------
    '''
    # 导入必要的包
    import os
    import numpy as np
    from PIL import Image
    import tensorflow as tf
    import matplotlib.pyplot as plt
    from CNNModel import deep_CNN
    
    N_CLASSES = 3
    
    img_dir = './Input_data/test/'
    log_dir = './Input_data'
    lists = ['guitar', 'house', 'rabbit']
    
    
    # 从测试集中随机挑选一张图片看测试结果
    def get_one_image(img_dir):
        imgs = os.listdir(img_dir)
        img_num = len(imgs)
        # print(imgs, img_num)
        idn = np.random.randint(0, img_num)
        image = imgs[idn]
        image_dir = img_dir + image
        print(image_dir)
        image = Image.open(image_dir)
        plt.imshow(image)
        plt.show()
        image = image.resize([28, 28])
        image_arr = np.array(image)
        return image_arr
    
    
    def test(image_arr):
        with tf.Graph().as_default():
            image = tf.cast(image_arr, tf.float32)
            image = tf.image.per_image_standardization(image)
            image = tf.reshape(image, [1, 28, 28, 3])
            # print(image.shape)
            p = deep_CNN(image, 1, N_CLASSES)
            logits = tf.nn.softmax(p)
            x = tf.placeholder(tf.float32, shape=[28, 28, 3])
            saver = tf.train.Saver()
            sess = tf.Session()
            sess.run(tf.global_variables_initializer())
            ckpt = tf.train.get_checkpoint_state(log_dir)
            if ckpt and ckpt.model_checkpoint_path:
                # print(ckpt.model_checkpoint_path)
                saver.restore(sess, ckpt.model_checkpoint_path)
                # 调用saver.restore()函数,加载训练好的网络模型
                print('Loading success')
            prediction = sess.run(logits, feed_dict={x: image_arr})
            max_index = np.argmax(prediction)
            print('预测的标签为:', max_index, lists[max_index])
            print('预测的结果为:', prediction)
    
    
    if __name__ == '__main__':
        img = get_one_image(img_dir)
        test(img)
    

    说明:测试集不是训练集的一部分!测试集没有参与训练!

    来看一下测试效果,结果如下

    显示随机的测试图片

    关闭图片后会出现测试结果

     

     

     

    2019.08.14

    看到有评论说测试代码总是只能测出一个结果,我特地用二分类做了一下简单的测试,不规范是真的,最终结果应该没什么问题。

     我把这份完整的代码包括数据集和训练模型参数打包放到github上,剩下的大家就根据实际情况修改吧!

     

    特别说明

    注意,真实训练的时候,测试数据集应该是和训练数据集不一样的,这里只是做个示例。

    总的说来,现在看这份代码,其实有很多不规范的地方,所以仅作参考

     


    数据库用的是CK+表情数据库中的原图

    放上完整的项目链接CNN(from github)

    2019-8-14用二分类做的小测试完整项目A-Simple-Two-Classification-Problem-Based-on-CNN


     

    展开全文
  • 几种常见图片格式优缺点分析

    千次阅读 2017-11-17 14:04:22
    构成点阵图的最小单位是象素,位图就是由象素阵列的排列来实现其显示效果的,每个象素有自己的颜色信息,在对位图图像进行编辑操作的时候,可操作的对象是每个象素,我们可以改变图像的色相、饱和度、明度,从而改变...

    有损vs无损

    图片文件格式有可能会对图片的文件大小进行不同程度的压缩,图片的压缩分为有损压缩和无损压缩两种。

    有损压缩。指在压缩文件大小的过程中,损失了一部分图片的信息,也即降低了图片的质量,并且这种损失是不可逆的,我们不可能从有一个有损压缩过的图片中恢复出全来的图片。常见的有损压缩手段,是按照一定的算法将临近的像素点进行合并。

    无损压缩。只在压缩文件大小的过程中,图片的质量没有任何损耗。我们任何时候都可以从无损压缩过的图片中恢复出原来的信息。

    索引色vs直接色

    计算机在表示颜色的时候,有两种形式,一种称作索引颜色(Index Color),一种称作直接颜色(Direct Color)。

    索引色。用一个数字来代表(索引)一种颜色,在存储图片的时候,存储一个数字的组合,同时存储数字到图片颜色的映射。这种方式只能存储有限种颜色,通常是256种颜色,对应到计算机系统中,使用一个字节的数字来索引一种颜色。

    直接色。使用四个数字来代表一种颜色,这四个数字分别代表这个颜色中红色、绿色、蓝色以及透明度。现在流行的显示设备可以在这四个维度分别支持256种变化,所以直接色可以表示2的32次方种颜色。当然并非所有的直接色都支持这么多种,为压缩空间使用,有可能只有表达红、绿、蓝的三个数字,每个数字也可能不支持256种变化之多。

    点阵图vs矢量图

    点阵图,也叫做位图,像素图。构成点阵图的最小单位是象素,位图就是由象素阵列的排列来实现其显示效果的,每个象素有自己的颜色信息,在对位图图像进行编辑操作的时候,可操作的对象是每个象素,我们可以改变图像的色相、饱和度、明度,从而改变图像的显示效果。点阵图缩放会失真,用最近非常流行的沙画来比喻最恰当不过,当你从远处看的时候,画面细腻多彩,但是当你靠的非常近的时候,你就能看到组成画面的每粒沙子以及每个沙粒的颜色。

    矢量图,也叫做向量图。矢量图并不纪录画面上每一点的信息,而是纪录了元素形状及颜色的算法,当你打开一付矢量图的时候,软件对图形象对应的函数进行运算,将运算结果[图形的形状和颜色]显示给你看。无论显示画面是大还是小,画面上的对象对应的算法是不变的,所以,即使对画面进行倍数相当大的缩放,其显示效果仍然相同(不失真)。

    BMP

    BitMap的缩写,是无损的、既支持索引色也支持直接色的、点阵图。

    这是一种比较老的图片格式。BMP是无损的,但同时这种图片格式几乎没有对数据进行压缩,所以BMP格式的图片通常具有较大的文件大小。虽然同时支持索引色和直接色是一个优点,但是太大的文件格式格式导致它几乎没有用武之地,现在除了在Windows操作系统中还比较常见之外,我们几乎看不到它。从上图中可以看到,在同样的图片质量下,BMP格式的图片文件大小是GIF格式的很多倍。

    GIF

    全称Graphics Interchange Format,采用LZW压缩算法进行编码。

    是无损的、采用索引色的、点阵图。GIF是无损的,采用GIF格式保存图片不会降低图片质量。但得益于数据的压缩,GIF格式的图片,其文件大小要远小于BMP格式的图片。文件小,是GIF格式的优点,同时,GIF格式还具有支持动画以及透明的优点。但,GIF格式仅支持8bit的索引色,即在整个图片中,只能存在256种不同的颜色。GIF格式适用于对色彩要求不高同时需要文件体积较小的场景,比如企业Logo、线框类的图等。因其体积小的特点,现在GIF被广泛的应用在各类网站中。

    JPEG

    JPEG是有损的、采用直接色的、点阵图。

    JPEG图片格式的设计目标,是在不影响人类可分辨的图片质量的前提下,尽可能的压缩文件大小。这意味着JPEG去掉了一部分图片的原始信息,也即是进行了有损压缩。JPEG的图片的优点,是采用了直接色,得益于更丰富的色彩,JPEG非常适合用来存储照片,用来表达更生动的图像效果,比如颜色渐变。与GIF相比,JPEG不适合用来存储企业Logo、线框类的图。因为有损压缩会导致图片模糊,而直接色的选用,又会导致图片文件较GIF更大。

    PNG-8

    PNG全称Portable Network Graphics,PNG-8是PNG的索引色版本。PNG-8是无损的、使用索引色的、点阵图。

    PNG是一种比较新的图片格式,PNG-8是非常好的GIF格式替代者,在可能的情况下,应该尽可能的使用PNG-8而不是GIF,因为在相同的图片效果下,PNG-8具有更小的文件体积。除此之外,PNG-8还支持透明度的调节,而GIF并不支持。 现在,除非需要动画的支持,否则我们没有理由使用GIF而不是PNG-8。当然了,PNG-8本身也是支持动画的,只是浏览器支持得不好,不像GIF那样受到广泛的支持。可以看到PNG-8具有更好的透明度支持。

    PNG-24

    PNG-24是PNG的直接色版本。

    PNG-24是无损的、使用直接色的、点阵图。无损的、使用直接色的点阵图,听起来非常像BMP,是的,从显示效果上来看,PNG-24跟BMP没有不同。PNG-24的优点在于,它压缩了图片的数据,使得同样效果的图片,PNG-24格式的文件大小要比BMP小得多。当然,PNG24的图片还是要比JPEG、GIF、PNG-8大得多。虽然PNG-24的一个很大的目标,是替换JPEG的使用。但一般而言,PNG-24的文件大小是JPEG的五倍之多,而显示效果则通常只能获得一点点提升。所以,只有在你不在乎图片的文件体积,而想要最好的显示效果时,才应该使用PNG-24格式。另外,PNG-24跟PNG-8一样,是支持图片透明度的。

    SVG

    全称Scalable Vector Graphics,是无损的、矢量图。

    SVG跟上面这些图片格式最大的不同,是SVG是矢量图。这意味着SVG图片由直线和曲线以及绘制它们的方法组成。当你放大一个SVG图片的时候,你看到的还是线和曲线,而不会出现像素点。这意味着SVG图片在放大时,不会失真,所以它非常适合用来绘制企业Logo、Icon等。 SVG是很多种矢量图中的一种,它的特点是使用XML来描述图片。借助于前几年XML技术的流行,SVG也流行了很多。使用XML的优点是,任何时候你都可以把它当做一个文本文件来对待,也就是说,你可以非常方便的修改SVG图片,你所需要的只需要一个文本编辑器。SVG并非只能绘制简单的Logo类的图片,它可以绘制出精致的图片的,比如下面这涨,嗯。

    WebP

    WebP是谷歌开发的一种新图片格式,WebP是同时支持有损和无损压缩的、使用直接色的、点阵图。

    从名字就可以看出来它是为Web而生的,什么叫为Web而生呢?就是说相同质量的图片,WebP具有更小的文件体积。现在网站上充满了大量的图片,如果能够降低每一个图片的文件大小,那么将大大减少浏览器和服务器之间的数据传输量,进而降低访问延迟,提升访问体验。在无损压缩的情况下,相同质量的WebP图片,文件大小要比PNG小26%;在有损压缩的情况下,具有相同图片精度的WebP图片,文件大小要比JPEG小25%~34%;WebP图片格式支持图片透明度,一个无损压缩的WebP图片,如果要支持透明度只需要22%的格外文件大小。想象Web上的图片之多,百分之几十的提升,是非常非常大的优化。只可惜,目前只有Chrome浏览器和Opera浏览器支持WebP格式,所以WebP的应用并不广泛。为了使用更先进的技术,比如WebP图片格式,来压缩互联网上传输的数据流量,谷歌甚至提供了Chrome Data Compression Proxy,设置了Chrome Data Compression Proxy作为Web代理之后,你访问的所有网站中的图片,在经过Proxy的时候,都会被转换成WebP格式,以降低图片文件的大小。

    referencePNG vs. GIF vs. JPEG vs. SVG - When best to use?https://zh.wikipedia.org/wiki/%E5%8F%AF%E7%B8%AE%E6%94%BE%E5%90%91%E9%87%8F%E5%9C%96%E5%BD%A2https://developers.google.com/speed/webp/ High Performance Browser Networking by Ilya Grigorik

    作者:赵鑫
    链接:https://www.zhihu.com/question/20028452/answer/142593276
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    展开全文
  • 缺点:我们会发现,有的比较大的图片一张图片就是满篇的代码!(一般的还是可以,但是对于微信小程序这种上传不能超过4M,而且小程序里图片还特别多的情况下,就会很大导致上传不成功) 推荐在线图片转base64地址: ...

    一、常见的,在线图片转base64

    找网上的在线图片转base64生成器,直接上传图片转!
    缺点:我们会发现,有的比较大的图片一张图片就是满篇的代码!(一般的还是可以,但是对于微信小程序这种上传不能超过4M,而且小程序里图片还特别多的情况下,就会很大导致上传不成功)

    推荐在线图片转base64地址:
    1、https://www.sojson.com/image2base64.html
    2、https://tool.css-js.com/base64.html

    二、QQ空间上传图片

    qq空间相册可以上传图片,上传好,直接右键复制链接就好了!
    缺点:生成的链接一般都是http://xxxxxx 因为HTTP 是明文传输的,存在可能被篡改内容的风险 ,对于微信小程序,一般是推荐使用 HTTPS,可以让小程序更加安全;

    三、传闻中的CSDN

    我没有试过这种方法,主要是我平时上传图片的时候右下角有水印,所以没有试

    四、百度

    方法:
    在这里插入图片描述
    上传图片就对了
    在这里插入图片描述
    右键新标签打开
    在这里插入图片描述
    右键复制地址就对了在这里插入图片描述

    优点:生成的是https://开头的链接,而且比较短!

    五、直接将图片上传到云开发存储中(!!个人觉得最好)

    使用云开发,上传图片到存储中,然后点击图片详情就有图片的下载地址

    在这里插入图片描述

    展开全文
  • 自己的Hexo博客加上图片

    万次阅读 2018-04-21 16:08:14
    自己的博客加上图片前言写博客的时候,有时候图片比文字更有说服力,比如:代码运行结果,本来一张图片解决的事情,不用图片的话,就得自己敲结果,然而别人不一定能看懂,这就非常尴尬了,所以说,在某些时候图片...

    给自己的博客加上图片


    前言

    写博客的时候,有时候图片比文字更有说服力,比如:代码运行结果,本来一张图片解决的事情,不用图片的话,就得自己敲结果,然而别人不一定能看懂,这就非常尴尬了,所以说,在某些时候图片的说服力还是很强大的,比如下面这张表达的意思。

    ​ 言归正传,我下面介绍一下我比较相中的办法,因为不用借助七牛等等,当然相比较而言的优缺点就忽略吧。

    解决办法

    1.首先把blog(hexo)目录下的_config.yml里的psot_asset_folder:设置为true

    2.在blog(hexo)目录下执行:

    npm install hexo-asset-image --save

    3.在blog(hexo)目录下Git Bash Here,运行hexo n "博客名"来生成md博客时,会在_post目录下看到一个与博客同名的文件夹。

    4.将想要上传的图片先扔到文件夹下,然后在博客中使用markdown的格式引入图片:

    
    ![你想要输入的替代文字](xxxx/图片名.jpg)

    ps:因为博客名和文件夹名字相同,所以不需要绝对路径,只要xxxx是文件夹的名字就可以了。

    5.然后,使用hexo g部署的时候,进入public\2018\04\19\index.html文件中查看相关字段,可以发现html标签内的语句是img src = "2018/04/19/xxxx/图片名.jpg"而不是img src="xxxx.图片名.jpg",这就成功了,当然前面步骤操作正确的话,这一步也不用检查。

    ​ 最后放一张帅照,并感谢Nubb,为你引流233333。


    展开全文
  • 网络图片加载库的特点及优势 Android 中图片处理的难点: oom内存溢出 图片尺寸和缩略图处理的平衡 网络图片的加载与缓存机制 今天学习两款比较优秀的开源图片处理库框架: Universal-ImageLoader的简介和特点: ...
  • 关于传统LSB的优缺点的分析

    千次阅读 2012-05-22 23:15:04
    在信息隐藏方面,我自己所学并不多,但是从做实验的过程中学到了不少,最后写实验报告的时候有个关于LSB算法的优缺点的分析。很多同学来问我,在这里,基于实验要求以及做完实验后的一些个人测试,我谨在这里做个...
  • Glide图片加载框架一、简介:Glide是一款谷歌官方推荐使用的强大图片加载框架,作者是bumptech,这个库被广泛运用在google的开源项目中。二、功能特点:1、图片异步加载,设置加载尺寸,设置加载动画,设置加载中和...
  • 并发编程的优缺点

    万次阅读 多人点赞 2019-10-03 21:50:50
    并发编程的优缺点 Java并发编程是整个Java开发体系中最难以理解,但也是最重要的知识点之一,因此学习起来比较费劲,从而导致很多人望而却步,但是无论是职场面试还是高并发高流量的系统的实现都离不开并发编程,...
  • http优缺点

    千次阅读 2019-11-07 11:41:40
    灵活可扩展:http非常灵活,在报文中没有做过多的限制,只要按照其规则可以自己定义字段,在传输中也不仅仅限于txt文本格式,也可以传输图片,视频,压缩包等等任意数据。 可靠性:因为http是基于tcp/ip传输的,...
  • 在android中的加载网络图片是一件十分令人头疼的事情,在网上有着许多关于加载网络图片的开源库,可以让我们十分方便的加载网络图片。在这里我主要介绍一下我自己在使用Volley, Picasso, Universal-Imageloader的...
  • 数据是网上下载的,后期打算自己采集图片再进行一次实验。数据集由三类图片构成,分别是bikes,person和bike_and_person,每类200张,一共600张。然后分成数据集和测试集,测试集450张,每类150张,训练集150张,每类...
  • 几款笔记软件的优缺点

    万次阅读 多人点赞 2018-07-27 18:19:37
    下面让我以一个程序员的角度,来说说他们的优缺点吧!(当然这个对比掺杂了我的个人情感,所以仅供参考啊!!!!) 我先说说对于笔记软件,我比较在意哪些地方啊! 1、层级结构,笔记结构要清晰明了。 2、复制粘贴时...
  • Android圆角图片和圆形图片实现总结

    万次阅读 多人点赞 2016-10-27 15:14:06
    Android研发中经常会遇见圆角或者圆形图片的展示,但是系统中ImageView并不能直接支持,需要我们自己做一些处理,来实现圆角图片或者圆形图片自己最近对这块的实现做了一下总结.
  • 剑指Offer–“你最大的缺点是什么”回答技巧及范例  问题分析:认识自己缺点是一个巨大的优点, 当HR问到你缺点的时候, 你的机会来了, 请快展示你的自知之明吧!你想把优点故意包装成缺点?   比方说把“对...
  • 缺点 一是 在训练上花费很少的时间(具体见下,训练只是缓存了训练集),但在预测上花费很多时间(有扩展的算法可以减少预测时间,以正确率作为代价,例如 Approximate Nearest Neighbor  (ANN) ,具体可参考 ...
  • scrapy框架的优缺点

    千次阅读 2019-11-18 18:12:39
    Scrapy是用纯Python实现一个为了爬取网站数据、提取结构性数据而编写的应用框架,用途非常广泛,用户只需要定制开发几个模块就可以轻松的实现一个爬虫,用来抓取网页内容以及各种图片,非常之方便。 尽管Scrapy原本...
  • //-----------------------------------------------------------------------------...// topic:关于嵌入式高端ARM核心板设计风格优缺点分析(作者:gooogleman) // 作者:gooogleman // 时间:2011.11.28 // 原文
  • 如何动态显示图片

    千次阅读 2016-05-16 21:18:14
    这种办法的优点是简单,缺点是当图片大小不一致的时候,图片显示会一大一小很难看。 第二种办法就是直接把位图图片画上去。优点是可以任意伸缩以后画上去,这样可以保证大小一致。很明显的缺点就是代码比较难,稍微...
  • 我在网络上看到的PDM的概念、定义、优缺点等相关信息  PDM的确是一种“管得很宽”的软件,凡是最终可以转换成计算机描述和存储的数据,它都可以一概管之,例如:产品结构和配置、零件定义及设计数据、CAD绘图文件...
  • 简单的说,就是可以把设计稿和图片转成前端代码。你或许会说,这个很多年前就有了啊,而且很不好用。但是!这个是由阿里巴巴推出的,所以规范程度应该会很高。 如果永久免费的话,我相信初中级的前端开发都要失业,...
  •  而OLED的像素则可以自己发光,不需要外部光源照亮,就好像能自己发光的桌子一样。通常认为,OLED在技术上比LCD更为先进,不过由于 他仍不成熟,与成本问题,LCD仍在现代的屏幕中发光发热。OLED能像素自己发光,...
  • JSONP跨域请求原理及优缺点

    万次阅读 多人点赞 2018-04-13 11:26:09
    一、JSONP与JSON的区别: JSONP 是一种非正式传输协议,该协议的一个要点...JSON 数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。 JSONP 和 JSON 的区别: JSON 是一种传输格式,而 JS...
  • Glide详细了解优缺点

    千次阅读 2018-01-02 20:59:45
    今天我们来学习一下其中一个Android主流的图片加载库的使用 - Glide 目录 目录 1. 简介 介绍:Glide,是Android中一个图片加载开源库 Google的开源项目 主要作用:实现图片加载 2. 功能特点 2.1 功能...
  • 另外,关于数据库的复制模式,还请同学们自行学习,太多了,这里说不清。该总结一下这种模式的优缺点的了,如下: 优点:减少数据库的压力,理论上提供无限高的读性能,间接提高业务(写)的性能,专用的查询、索引、...
  • 该项目在GitHub开源,支持生成图片、分享二维码和在线访问,朋友们可以在该网址上传贡献自己的“甜言蜜语”。 感情不易,多些真诚,少些套路,真诚胜过一切谋略! GitHub网址:...
  • GAN原理,优缺点、应用总结

    万次阅读 多人点赞 2017-12-20 21:16:05
    GAN原理、优缺点、应用简要总结
  • 网络框架的优缺点

    千次阅读 2017-01-18 15:54:56
    网络框架 AFinal ... ...自动异步请求,不会造成主线程...缺点: 对HTTP请求没有任何缓存策略,不符合HTTP缓存协议不提供请求取消功能请求无优先级概念未修复HttpUrlConnection的BUG Volley https://github.co
  • 机器学习 —— 各类算法优缺点总结

    千次阅读 2018-02-17 09:40:33
    在我们日常生活中所用到的推荐系统、智能图片美化应用和聊天机器人等应用中,各种各样的机器学习和数据处理算法正尽职尽责地发挥着自己的功效。本文筛选并简单介绍了一些最常见算法类别,还为每一个类别列出了一些...
  • 各种电子书格式及优缺点

    万次阅读 2018-10-07 09:53:37
    各种电子书格式及优缺点 友益文书整理于2016年7月 &nbsp; 网上也有很多类似的文章了,但由于该类文章由于写的时间都较早,很多之前流行的电子书格式现在无人使用或对应的公司已经倒闭,无人维护了。因此重新...
  • Android Volley完全解析(二),使用Volley加载网络图片

    万次阅读 多人点赞 2014-04-17 09:23:22
    本篇文章中我们即将学习关于Volley更加高级的用法,如何你还没有看过我的上一篇文章的话,建议先去阅读Android Volley完全解析(一),初识Volley的基本用法。 在上篇文章中有提到过,Volley是将AsyncHttpClient和...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 129,453
精华内容 51,781
关键字:

关于自己缺点的图片