【深度学习】Cifar-10-探究不同的改进策略对分类准确率提高

cifar10数据集上进行图片分类,基于tensorflow框架,

旨在探究不同的改进策略对分类准确率的影响,如何一步步得提高准确率 

 

一、问题描述

当我们在处理图像识别或者图像分类或者其他机器学习任务的时候,我们总是迷茫于做出哪些改进能够提升模型的性能(识别率、分类准确率)。。。或者说我们在漫长而苦恼的调参过程中到底调的是哪些参数。。。所以,我花了一部分时间在公开数据集CIFAR-10 [1] 上进行探索,来总结出一套方法能够快速高效并且有目的性地进行网络训练和参数调整。

CIFAR-10数据集有60000张图片,每张图片均为分辨率为32*32的彩色图片(分为RGB3个信道)。CIFAR-10的分类任务是将每张图片分成青蛙、卡车、飞机等10个类别中的一个类别。本文主要使用基于卷积神经网络的方法(CNN)来设计模型,完成分类任务。

首先,为了能够在训练网络的同时能够检测网络的性能,我对数据集进行了训练集/验证集/测试集的划分。训练集主要用户进行模型训练,验证集主要进行参数调整,测试集主要进行模型性能的评估。因此,我将60000个样本的数据集分成了,45000个样本作为训练集,5000个样本作为验证集,10000个样本作为测试集。接下来,我们一步步来分析,如果进行模型设计和改进。

 

二、搭建最简单版本的CNN

对于任何的机器学习问题,我们一上来肯定是采用最简单的模型,一方面能够快速地run一个模型,以了解这个任务的难度,另一方面能够有一个baseline版本的模型,利于进行对比实验。所以,我按照以往经验和网友的推荐,设计了以下的模型。


模型的输入数据是网络的输入是一个4维tensor,尺寸为(128, 32, 32, 3),分别表示一批图片的个数128、图片的宽的像素点个数32、高的像素点个数32和信道个数3。首先使用多个卷积神经网络层进行图像的特征提取,卷积神经网络层的计算过程如下步骤:

  1. 卷积层1:卷积核大小3*3,卷积核移动步长1,卷积核个数64,池化大小2*2,池化步长2,池化类型为最大池化,激活函数ReLU。
  2. 卷积层2:卷积核大小3*3,卷积核移动步长1,卷积核个数128,池化大小2*2,池化步长2,池化类型为最大池化,激活函数ReLU。
  3. 卷积层3:卷积核大小3*3,卷积核移动步长1,卷积核个数256,池化大小2*2,池化步长2,池化类型为最大池化,激活函数ReLU。
  4. 全连接层:隐藏层单元数1024,激活函数ReLU。
  5. 分类层:隐藏层单元数10,激活函数softmax。

参数初始化,所有权重矩阵使用random_normal(0.0, 0.001),所有偏置向量使用constant(0.0)。使用cross entropy作为目标函数,使用Adam梯度下降法进行参数更新,学习率设为固定值0.001。


该网络是一个有三层卷积层的神经网络,能够快速地完成图像地特征提取。全连接层用于将图像特征整合成分类特征,分类层用于分类。cross entropy也是最常用的目标函数之一,分类任务使用cross entropy作为目标函数非常适合。Adam梯度下降法也是现在非常流行的梯度下降法的改进方法之一,学习率过大会导致模型难以找到较优解,设置过小则会降低模型训练效率,因此选择适中的0.001。这样,我们最基础版本的CNN模型就已经搭建好了,接下来进行训练和测试以观察结果。

训练5000轮,观察到loss变化曲线、训练集准确率变化曲线和验证集准确率变化曲线如下图。测试集准确率为69.36%

结果分析:首先我们观察训练loss(目标函数值)变化,刚开始loss从200不断减小到接近0,但是在100轮左右开始出现震荡,并且随着训练幅度越来越大,说明模型不稳定。然后观察训练集和验证集的准确率,发现训练集准确率接近于1,验证集准确率稳定在70%左右,说明模型的泛化能力不强并且出现了过拟合情况。最后评估测试集,发现准确率为69.36%,也没有达到很满意的程度,说明我们对模型需要进行很大的改进,接下来进行漫长的调参之旅吧!

 

三、数据增强有很大的作用

使用数据增强技术(data augmentation),主要是在训练数据上增加微小的扰动或者变化,一方面可以增加训练数据,从而提升模型的泛化能力,另一方面可以增加噪声数据,从而增强模型的鲁棒性。主要的数据增强方法有:翻转变换 flip、随机修剪(random crop)、色彩抖动(color jittering)、平移变换(shift)、尺度变换(scale)、对比度变换(contrast)、噪声扰动(noise)、旋转变换/反射变换 (rotation/reflection)等,可以参考Keras的官方文档 [2] 。获取一个batch的训练数据,进行数据增强步骤之后再送入网络进行训练。


我主要做的数据增强操作有如下方面:

  1. 图像切割:生成比图像尺寸小一些的矩形框,对图像进行随机的切割,最终以矩形框内的图像作为训练数据。
  2. 图像翻转:对图像进行左右翻转。
  3. 图像白化:对图像进行白化操作,即将图像本身归一化成Gaussian(0,1)分布。

为了进行对比实验,观测不同数据增强方法的性能,实验1只进行图像切割,实验2只进行图像翻转,实验3只进行图像白化,实验4同时进行这三种数据增强方法,同样训练5000轮,观察到loss变化曲线、训练集准确率变化曲线和验证集准确率变化曲线对比如下图。

结果分析:我们观察训练曲线和验证曲线,很明显地发现图像白化的效果好,其次是图像切割,再次是图像翻转,而如果同时使用这三种数据增强技术,不仅能使训练过程的loss更稳定,而且能使验证集的准确率提升至82%左右,提升效果十分明显。而对于测试集,准确率也提升至80.42%。说明图像增强确实通过增加训练集数据量达到了提升模型泛化能力以及鲁棒性的效果,从准确率上看也带来了将近10%左右的提升,因此,数据增强确实有很大的作用。但是对于80%左右的识别准确率我们还是不够满意,接下来继续调参。

 

四、从模型入手,使用一些改进方法

接下来的步骤是从模型角度进行一些改进,这方面的改进是诞生论文的重要区域,由于某一个特定问题对某一个模型的改进千变万化,没有办法全部去尝试,因此一般会实验一些general的方法,比如批正则化(batch normalization)、权重衰减(weight decay)。我这里实验了4种改进方法,接下来依次介绍。

  1. 权重衰减(weight decay):对于目标函数加入正则化项,限制权重参数的个数,这是一种防止过拟合的方法,这个方法其实就是机器学习中的l2正则化方法,只不过在神经网络中旧瓶装新酒改名为weight decay [3]。
  2. dropout:在每次训练的时候,让某些的特征检测器停过工作,即让神经元以一定的概率不被激活,这样可以防止过拟合,提高泛化能力 [4]。
  3. 批正则化(batch normalization):batch normalization对神经网络的每一层的输入数据都进行正则化处理,这样有利于让数据的分布更加均匀,不会出现所有数据都会导致神经元的激活,或者所有数据都不会导致神经元的激活,这是一种数据标准化方法,能够提升模型的拟合能力 [5]。
  4. LRN:LRN层模仿生物神经系统的侧抑制机制,对局部神经元的活动创建竞争机制,使得响应比较大的值相对更大,提高模型泛化能力。

为了进行对比实验,实验1只使用权重衰减,实验2使用权重衰减+dropout,实验3使用权重衰减+dropout+批正则化,实验4使用权重衰减+dropout+批正则化+LRN,同样都训练5000轮,观察到loss变化曲线、训练集准确率变化曲线和验证集准确率变化曲线对比如下图。

结果分析:我们观察训练曲线和验证曲线,随着每一个模型提升的方法,都会使训练集误差和验证集准确率有所提升,其中,批正则化技术和dropout技术带来的提升非常明显,而如果同时使用这些模型提升技术,会使验证集的准确率从82%左右提升至88%左右,提升效果十分明显。而对于测试集,准确率也提升至85.72%。我们再注意看左图,使用batch normalization之后,loss曲线不再像之前会出现先下降后上升的情况,而是一直下降,这说明batch normalization技术可以加强模型训练的稳定性,并且能够很大程度地提升模型泛化能力。所以,如果能提出一种模型改进技术并且从原理上解释同时也使其适用于各种模型,那么就是非常好的创新点,也是我想挑战的方向。现在测试集准确率提升至85%左右,接下来我们从其他的角度进行调参。

 

五、变化的学习率,进一步提升模型性能

在很多关于神经网络的论文中,都采用了变化学习率的技术来提升模型性能,大致的想法是这样的:

  1. 首先使用较大的学习率进行训练,观察目标函数值和验证集准确率的收敛曲线。
  2. 如果目标函数值下降速度和验证集准确率上升速度出现减缓时,减小学习率。
  3. 循环步骤2,直到减小学习率也不会影响目标函数下降或验证集准确率上升为止。

为了进行对比实验,实验1只使用0.01的学习率训练,实验2前10000个batch使用0.01的学习率,10000个batch之后学习率降到0.001,实验3前10000个batch使用0.01的学习率,10000~20000个batch使用0.001的学习率,20000个batch之后学习率降到0.0005。同样都训练5000轮,观察到loss变化曲线、训练集准确率变化曲线和验证集准确率变化曲线对比如下图。

结果分析:我们观察到,当10000个batch时,学习率从0.01降到0.001时,目标函数值有明显的下降,验证集准确率有明显的提升,而当20000个batch时,学习率从0.001降到0.0005时,目标函数值没有明显的下降,但是验证集准确率有一定的提升,而对于测试集,准确率也提升至86.24%。这说明,学习率的变化确实能够提升模型的拟合能力,从而提升准确率。学习率在什么时候进行衰减、率减多少也需要进行多次尝试。一般在模型基本成型之后,使用这种变化的学习率的方法,以获取一定的改进,精益求精。

 

六、加深网络层数,会发生什么事情?

现在深度学习大热,所以,在计算资源足够的情况下,想要获得模型性能的提升,大家最常见打的想法就是增加网络的深度,让深度神经网络来解决问题,但是简单的网络堆叠不一定就能达到很好地效果,抱着深度学习的想法,我按照plain-cnn模型 [6],我做了接下来的实验。

  1. 卷积层1:卷积核大小3*3,卷积核移动步长1,卷积核个数16,激活函数ReLU,使用batch_normal和weight_decay,接下来的n层,卷积核大小3*3,卷积核移动步长1,卷积核个数16,激活函数ReLU,使用batch_normal和weight_decay。
  2. 卷积层2:卷积核大小3*3,卷积核移动步长2,卷积核个数32,激活函数ReLU,使用batch_normal和weight_decay,接下来的n层,卷积核大小3*3,卷积核移动步长1,卷积核个数32,激活函数ReLU,使用batch_normal和weight_decay。
  3. 卷积层3:卷积核大小3*3,卷积核移动步长2,卷积核个数64,激活函数ReLU,使用batch_normal和weight_decay,接下来的n层,卷积核大小3*3,卷积核移动步长1,卷积核个数64,激活函数ReLU,使用batch_normal和weight_decay。
  4. 池化层:使用全局池化,对64个隐藏单元分别进行全局池化。
  5. 全连接层:隐藏层单元数10,激活函数softmax,使用batch_normal和weight_decay。

为了进行对比实验,进行4组实验,每组的网络层数分别设置8,14,20和32。同样都训练5000轮,观察到loss变化曲线、训练集准确率变化曲线和验证集准确率变化曲线对比如下图。

结果分析:我们惊讶的发现,加深了网络层数之后,性能反而下降了,达不到原来的验证集准确率,网络层数从8层增加到14层,准确率有所上升,但从14层增加到20层再增加到32层,准确率不升反降,这说明如果网络层数过大,由于梯度衰减的原因,导致网络性能下降,因此,需要使用其他方法解决梯度衰减问题,使得深度神经网络能够正常work。

 

七、终极武器,残差网络

2015年,Microsoft用残差网络 [7] 拿下了当年的ImageNet,这个残差网络就很好地解决了梯度衰减的问题,使得深度神经网络能够正常work。由于网络层数加深,误差反传的过程中会使梯度不断地衰减,而通过跨层的直连边,可以使误差在反传的过程中减少衰减,使得深层次的网络可以成功训练,具体的过程可以参见其论文[7]。

通过设置对比实验,观察残差网络的性能,进行4组实验,每组的网络层数分别设置20,32,44和56。观察到loss变化曲线和验证集准确率变化曲线对比如下图。

同的改进策略对分类准确率的影响,如何一步步得提高准确率。

 

代码:

import numpy as np
# 序列化和反序列化
import pickle
from sklearn.preprocessing import OneHotEncoder
import warnings
warnings.filterwarnings('ignore')
import tensorflow as tf#数据加载def unpickle(file):import picklewith open(file, 'rb') as fo:dict = pickle.load(fo, encoding='ISO-8859-1')return dict
labels = []
X_train = []
for i in range(1,6):data = unpickle('./cifar-10-batches-py/data_batch_%d'%(i))labels.append(data['labels'])X_train.append(data['data'])
# 将list类型转换为ndarray
X_train = np.array(X_train)X_train = np.transpose(X_train.reshape(-1,3,32,32),[0,2,3,1]).reshape(-1,3072)y_train = np.array(labels).reshape(-1)# reshape
X_train = X_train.reshape(-1,3072)# 目标值概率
one_hot = OneHotEncoder()
y_train =one_hot.fit_transform(y_train.reshape(-1,1)).toarray()# 测试数据加载
test = unpickle('./cifar-10-batches-py/test_batch')
X_test = test['data']
X_test = np.transpose(X_test.reshape(-1,3,32,32),[0,2,3,1]).reshape(-1,3072)
y_test = one_hot.transform(np.array(test['labels']).reshape(-1,1)).toarray()# 从总数据中获取一批数据
index = 0
def next_batch(X,y):global indexbatch_X = X[index*128:(index+1)*128]batch_y = y[index*128:(index+1)*128]index+=1if index == 390:index = 0return batch_X,batch_y# 构建神经网络X = tf.placeholder(dtype=tf.float32,shape = [None,3072])
y = tf.placeholder(dtype=tf.float32,shape = [None,10])
kp = tf.placeholder(dtype=tf.float32)### !!!变量
def gen_v(shape,std = 5e-2):return tf.Variable(tf.truncated_normal(shape = shape,stddev=std))def conv(input_,filter_,b):conv = tf.nn.conv2d(input_,filter_,strides=[1,1,1,1],padding='SAME') + bconv = tf.layers.batch_normalization(conv,training=True)conv = tf.nn.relu(conv)return tf.nn.max_pool(conv,[1,3,3,1],[1,2,2,1],'SAME')def net_work(X,kp):
#     形状改变,4维input_ = tf.reshape(X,shape = [-1,32,32,3])
#     第一层filter1 = gen_v(shape = [3,3,3,64])b1 = gen_v(shape = [64])pool1 = conv(input_,filter1,b1)#     第二层filter2 = gen_v([3,3,64,128])b2 = gen_v(shape = [128])pool2 = conv(pool1,filter2,b2)#     第三层filter3 = gen_v([3,3,128,256])b3 = gen_v([256])pool3 = conv(pool2,filter3,b3)#     第一层全连接层dense = tf.reshape(pool3,shape = [-1,4*4*256])fc1_w = gen_v(shape = [4*4*256,1024])fc1_b = gen_v([1024])bn_fc_1 = tf.layers.batch_normalization(tf.matmul(dense,fc1_w) + fc1_b,training=True)relu_fc_1 = tf.nn.relu(bn_fc_1)
#     fc1.shape = [-1,1024]#     dropoutdp = tf.nn.dropout(relu_fc_1,keep_prob=kp)#     fc2 输出层out_w = gen_v(shape = [1024,10])out_b = gen_v(shape = [10])out = tf.matmul(dp,out_w) + out_breturn out# 损失函数准确率&最优化out = net_work(X,kp)
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labels=y,logits=out))
# 准确率
y_ = tf.nn.softmax(out)
# equal 相当于 == 
equal = tf.equal(tf.argmax(y,axis = -1),tf.argmax(y_,axis = 1))
accuracy = tf.reduce_mean(tf.cast(equal,tf.float32))opt = tf.train.AdamOptimizer(0.01).minimize(loss)
opt# 开启训练saver = tf.train.Saver()# config = tf.ConfigProto(allow_soft_placement=True)
# config.gpu_options.allocator_type = 'BFC'
# config.gpu_options.per_process_gpu_memory_fraction = 0.80epoches = 100
with tf.Session() as sess:sess.run(tf.global_variables_initializer())for i in range(epoches):batch_X,batch_y = next_batch(X_train,y_train)opt_,loss_ ,score_train= sess.run([opt,loss,accuracy],feed_dict = {X:batch_X,y:batch_y,kp:0.5})print('iter count:%d。mini_batch loss:%0.4f。训练数据上的准确率:%0.4f。测试数据上准确率:...'%(i+1,loss_,score_train ))if score_train > 0.6:saver.save(sess,'./model/estimator',i+1)saver.save(sess,'./model/estimator',i+1)for i in range(77):X_test1,y_test1=  next_batch(X_test,y_test)score_test = sess.run([accuracy],feed_dict = {X:X_test1,y:y_test1,kp:1.0})print('测试数据上的准确率:',score_test,y_[0],y[0])epoches = 1100
with tf.Session() as sess:saver.restore(sess,'./model/estimator-100')for i in range(100,epoches):batch_X,batch_y = next_batch(X_train,y_train)opt_,loss_ ,score_train= sess.run([opt,loss,accuracy],feed_dict = {X:batch_X,y:batch_y,kp:0.5})print('iter count:%d。mini_batch loss:%0.4f。训练数据上的准确率:%0.4f。测试数据上准确率:%0.4f'%(i+1,loss_,score_train,score_test))if score_train > 0.6:saver.save(sess,'./model/estimator',i+1)saver.save(sess,'./model/estimator',i+1)if (i%100 == 0) and (i != 100):score_test = sess.run(accuracy,feed_dict = {X:X_test,y:y_test,kp:1.0})print('----------------测试数据上的准确率:---------------',score_test)

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/456012.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Acer 4750 安装黑苹果_黑苹果 MacOS 10.15 Catalina安装教程

一、准备工作一个8G以上的U盘(安装 10.15 Catalina 必须要16G及以上的U盘 );Mac OS镜像、TransMac(刻录工具)、DiskGenius(分区工具)、EasyUEFI(引导工区)、EFI驱动文件。安装工具获取方式:关注公众号【远景论坛】,回复:黑苹果二、…

帧内16*16模式的宏块数据传输顺序

如果宏块以16*16帧内模式编码,那么块-1首先被传输,携带的信息是每个4*4亮度块的DC系数。然后,亮度残差块0-15被传输(此时,16*16帧内宏块的DC系数为零)。对于亮度分量Cb和Cr,16和17块携带DC系数的…

pcie1 4 速度_太阳系行星们谁转得最快?八大行星自转速度排行榜,地球排第五...

不知道大家有没有玩儿过陀螺呢?玩儿陀螺的技术如果很好的话,它可以在地上飞快地旋转并且能够旋转很长的时间。有趣的是,宇宙中的很多星球就像陀螺一样绕着一个中心轴旋转着。这就是星球的自转。在太阳系中有八颗大行星,它们都在自…

分布式资本沈波:未来区块链杀手级应用将出现在“+区块链”

雷锋网5月22日报道,日前“区块链技术和应用峰会”在杭州国际博览中心举行。会上,分布式资本创始管理人沈波作了《区块链的投资现状与发展趋势》演讲。 沈波表示,由于区块链的共识机制和无法篡改两大特点,它在各行各业皆有应用潜力…

帧间预测小记

帧间预测后,在比特流中会有相应的信息:残差信息,运动矢量信息,所选的模式。 宏块的色度分量分辨率是亮度分辨率的一半(Cr和Cb),水平和垂直均一半。色度块采用和亮度块一致的分割模式&#xff0…

ImageJ Nikon_科研论文作图之ImageJ

各位读者朋友们又见面了,今天给大家介绍一款图片处理软件——ImageJ,这是一款免费的科学图像分析工具,广泛应用于生物学研究领域。ImageJ软件能够对图像进行缩放、旋转、扭曲、模糊等处理,也可计算选定区域内分析对象的一系列几何…

【urllib】url编码问题简述

对url编解码总结 需要用到urllib库中的parse模块 import urllib.parse # Python3 url编码 print(urllib.parse.quote("天天")) # Python3 url解码 print(urllib.parse.unquote("%E5%A4%E5%A4%")) urlparse() # urllib.parse.urlparse(urlstring,scheme,…

独家| ChinaLedger白硕:区块链中的隐私保护

隐私问题一直是区块链应用落地的障碍问题之一,如何既能满足监管,又能不侵害数据隐私,是行业都在攻克的问题。那么,到底隐私问题为何难?有什么解决思路,以及实践创新呢?零知识证明、同态加密等技…

手机处理器排行榜2019_手机处理器AI性能排行榜出炉,高通骁龙第一,华为排在第十名...

↑↑↑击上方"蓝字"关注,每天推送最新科技新闻安兔兔在近日公布了今年四月份Android手机处理器AI性能排行榜,榜单显示高通骁龙865处理器的AI性能在Android阵营中排在第一名——该处理器的AI性能得分接近46万分,今年的小米10、三星G…

芯片支持的且会被用到的H.264特性 预测编码基本原理

视频压缩: 1.H.264基本档次和主要档次;2.CAVLC熵编码,即基于上下文的自适应变长编码;(不支持CABAC,即基于上下文的自适应算术编码)分辨率:仅用到1080p60,即分辨率为1920*…

MongoDB 数据库 【总结笔记】

一、MongoDB 概念解析 什么是MongoDB? ​ 1、MongoDB是有C语言编写的,是一个基于分布式文件存储的开源数据库系统,在高负载的情况下,添加更多节点,可以保证服务器的性能 ​ 2、MongoDB为web应用提供了高性能的数据存储…

PHP 函数截图 哈哈哈

转载于:https://www.cnblogs.com/bootoo/p/6714676.html

2016年光伏电站交易和融资的十大猜想

1领跑者计划备受关注,竞价上网或从试点开始 领跑者计划规模大,上网条件好,又有政府背书,虽说价格也不便宜,但省去很多隐性成本,对于致力于规模化发展的大型企业来说仍是首要选择。同时,从能源管…

loading gif 透明_搞笑GIF:有这样的女朋友下班哪里都不想去

原标题:搞笑GIF:有这样的女朋友下班哪里都不想去这样的广场舞看着不凉快吗?大哥慢点,机器经受不住你这样的速度求孩子的心里阴影面积生孩子就是用来玩的。有这样的媳妇做饭,下班哪里也不想去1.领导在门外用门夹核桃&am…

eigen库安装_OpenCV+Eigen上位机程序移植(七十一)

1、给硬盘分区现在小伙伴们对于给电脑硬盘分区想必比较头疼,给电脑硬盘分区分为两种情况,一是在安装系统之前给系统硬盘分区,二是在安装系统之后给硬盘分区,我们现在购买的品牌机和笔记本的用户比较多,而且笔记本和品牌…

【实战操作】使用FFmpeg将一个视频文件中音频合成到另一个视频中 只需三秒

直接进入主题 主要参数: -i——设置输入文件名。-f——设置输出格式。-y——若输出文件已存在时则覆盖文件。-fs——超过指定的文件大小时则结束转换。-t——指定输出文件的持续时间,以秒为单位。-ss——从指定时间开始转换,以秒为单位。-t从…

苹果依旧强大 物联网领域举足轻重

近几天科技界最火爆的话题就是苹果和谷歌两家公司,一是苹果发布财报,二是谷歌人工智能围棋战胜欧洲冠军以及谷歌母公司Alphabet或将超越苹果,成为世界上市值最大的公司。而在我眼里,未来五年内依然属于苹果。回顾下苹果公司公布的…

linux之SQL语句简明教程---SUBSTRING

SQL 中的 substring 函数是用来抓出一个栏位资料中的当中一部分。这个函数的名称在不同的资料库中不全然一样: MySQL: SUBSTR( ), SUBSTRING( )Oracle: SUBSTR( )SQL Server: SUBSTRING( )最经常使用到的方式例如以下 (在这里我们用 SUBSTR( ) 为例): SU…

listview 每行后面的小箭头_主卧带小衣帽装修,这几个装修方案,你喜欢哪个?...

南京装修交流圈 - 知乎​www.zhihu.com现在大部分人购买的房子面积还是普遍偏小的,每行每业都有自己的服装讲究,不同的场地、见不同的人等等。家中能有一个衣帽间自然也就是心中所向往和追求的。能够有独立的一个房间来做衣帽间当然更好,今天…

【养成好习惯】使用pipreqs导出本项目使用的环境

pipreqs pipreqs可以帮你找到当前项目的所有组件及其版本。就是当别人给你一个程序的时候,你要在自己电脑上运行起来,就需要安装程序所依赖的组件,总不能自己一个一个找吧。 # 安装 pip install -i https://pypi.tuna.tsinghua.edu.cn/simpl…