ResNet网络详解与keras实现

ResNet网络详解与keras实现

  • ResNet网络详解与keras实现
      • Resnet网络的概览
      • Pascal_VOC数据集
        • 第一层目录
        • 第二层目录
        • 第三层目录
      • 梯度退化
      • Residual Learning
      • Identity vs Projection Shortcuts
      • Bottleneck architecture
      • Resnet网络构建表
      • ResNet论文结果
        • 为了搭建Resnet网络我们使用了以下策略
        • 整个代码的流程如下
      • 实验结果
      • 实验结果分析
      • 本博客相关引用

本博客旨在给经典的ResNet网络进行详解与代码实现,如有不足或者其他的见解,请在本博客下面留言。

Resnet网络的概览

  1. 为了解决训练很深的网络时候出现的梯度退化(gradient degradation)的问题,Kaiming He提出了Resnet结构。由于使用了残差学习的方法(Resuidal learning),使得网络的层数得到了大大的提升。
  2. ResNet由于使用了shortcut,把原来需要学习逼近的未知函数H(x)恒等映射(Identity mapping),变成了逼近F(x)=H(x)-x的一个函数。作者认为这两种表达的效果相同,但是优化的难度却并不相同,作者假设F(x)的优化会比H(x)简单的多。这一想法也是源于图像处理中的残差向量编码,通过一个reformulation,将一个问题分解成多个尺度直接的残差问题,能够很好的起到优化训练的效果。
  3. ResNet针对较深(层数大于等于50)的网络提出了BottleNeck的结构,这个结构可以减少运算的时间复杂度
  4. ResNet里存在两种shortcut,Identity shortcut & Projection shortcut。Identity shortcut使用零填充的方式保证其纬度不变,而Projection shortcut则具有下面的形式y=F(x,Wi)+Wsx来匹配纬度的变换。
  5. ResNet这个模型在图像处理的相关任务中具有很好的泛化性,在2015年的ImageNet Recognization,ImageNet detection,ImageNet localization,COCO detection,COCO segmentation等等任务上取得第一的成绩。

在本篇博客中,将对Resnet的结构进行详细的解释,并用代码实现ResNet的网络结构。同时,本文还将引入另一篇论文<>,来更加深入的理解Resnet。本文使用VOC2012的数据集进行网络的训练,验证,与测试。为了快速开发,本次我们把Keras作为代码的框架。

Pascal_VOC数据集

Pascal VOC为图像识别,检测与分割提供了一整套标准化的优秀的数据集,每一年都会举办一次图像识别竞赛。下面是VOC2012,训练集(包括验证集)的下载地址。

VOC2012里面有20类物体的图片,图片总共有1.7万张。我把数据集分成了3个部分,训练集,验证集,测试集,比例为8:1:1。
下面是部分截图:

第一层目录

第一层目录


第二层目录

第二层目录


第三层目录

第三层目录


接着我们使用keras代码来使用这个数据集,代码如下:

IM_WIDTH=224 #图片宽度
IM_HEIGHT=224 #图片高度
batch_size=32 #批的大小# train data
train_datagen = ImageDataGenerator(width_shift_range=0.1,height_shift_range=0.1,shear_range=0.1,zoom_range=0.1,horizontal_flip=True,rescale=1./255
)
train_generator = train_datagen.flow_from_directory(train_root,target_size=(IM_WIDTH, IM_HEIGHT),batch_size=batch_size,shuffle=True
)# vaild data
vaild_datagen = ImageDataGenerator(width_shift_range=0.1,height_shift_range=0.1,shear_range=0.1,zoom_range=0.1,horizontal_flip=True,rescale=1./255
)
vaild_generator = train_datagen.flow_from_directory(vaildation_root,target_size=(IM_WIDTH, IM_HEIGHT),batch_size=batch_size,
)# test data
test_datagen = ImageDataGenerator(rescale=1./255
)
test_generator = train_datagen.flow_from_directory(test_root,target_size=(IM_WIDTH, IM_HEIGHT),batch_size=batch_size,
)

我使用了3个ImageDataGenerator,分别来使用训练集,验证集与测试集的数据。使用ImageDataGenerator需要导入相应的模块,==from keras.preprocessing.image import ImageDataGenerator==。ImageDataGenrator可以用来做数据增强,提高模型的鲁棒性.它里面提供了许多变换,包括图片旋转,对称,平移等等操作。里面的flow_from_directory方法可以从相应的目录里面批量获取图片,这样就可以不用一次性读取所有图片(防止内存不足)。

梯度退化

按照我们的惯性思维,一个网络越深则这个网络就应该具有更好的学习能力,而梯度退化是指下面一种现象:随着网络层数的增加,网络的效果先是变好到饱和,然后立即下降的一个现象。在这里,我们引用一幅来自Resnet里面的图片,更加直观的理解这个现象:
grad_degradation
从上图我们可以看出,一个56层的网络的训练误差和测试误差都大于一个20层的网络。

Residual Learning

resnet_block

为了解决梯度退化的问题,论文中提出了Residual learning这个方法,它通过构造一个Residual block来完成。如图Figure 2所示,引入残差结构以后,把原来需要学习逼近的未知函数H(x)恒等映射(Identity mapping),变成了逼近F(x)=H(x)-x的一个函数。作者认为这两种表达的效果相同,但是优化的难度却并不相同,作者假设F(x)的优化会比H(x)简单的多。这一想法也是源于图像处理中的残差向量编码,通过一个reformulation,将一个问题分解成多个尺度直接的残差问题,能够很好的起到优化训练的效果。

上图的恒等映射,是把一个输入x和其堆叠了2次后的输出F(x)的进行元素级和作为总的输出。因此它没有增加网络的运算复杂度,而且这个操作很容易被现在的一些常用库执行(e.g.,Caffe,tensorflow)。

下面是一张没有使用普通图(plain,即没有加入恒等映射的图),与一张有shortcut图的对比:
resnet
最左边的图为经典的VGG-19图的网络结构,中间的图是一个类似于VGG-19的34层的普通图,最右边的图是34层的带有恒等映射的Resnet网络图。其中黑色的实线代表的是同一纬度(即卷积核的个数相同)下的恒等映射。而虚线指的是不同维度间(卷积核的个数不同)的恒等映射。

Identity vs Projection Shortcuts

除了最简单的Identity shortcuts(直接进行同纬度的元素级相加),论文还研究了Projection shortcuts($ y=F(x,{W_i})+W_sx$).论文研究了以下3种情况:

i. 对于纬度没有变化的连接进行直接相连,对于纬度增加的连接则通过补零填充后进行连接。由于shortcuts是恒等的,因此这个连接本身不会带来额外的参数。

ii. 对于纬度没有变化的连接进行直接相连,对于纬度增加的连接则通过投影相连,投影相连会增加参数

iii. 对于所有的连接都采取投影相连。

作者对以上三种情况都进行了研究,发现iii的效果比ii好一点点点(marginly better),发现ii的效果比i的效果好一点。这是因为$W_s$中带来的额外参数所带来的效果。

Bottleneck architecture

resnet
如上图右边所示,作者在研究更深层次(层数大于50)的网络的时候,使用了Bottleneck这个网络结构。我觉得作者可能是参考了goolenet里面的Inception结构。我们可以看到在Bottleneck中,第一个1x1的卷积层用来在降低纬度(用来降低运算复杂度),而后一个的1x1的卷积层则用来增加纬度,使其保持与原来的输入具有相同的纬度。(从而可以进行恒等映射)。

Resnet网络构建表

resnet_summary

Tabel 1

上图是一个Resnet的网络构建表,它显示了resnet是怎么构成的。同时这个表还提供了各个网络的运算浮点数,虽然resnet的层数比较深,但是它的运算量都小于VGG-19(19.6x10的9次方)。

ResNet论文结果:

res1

上图左边是普通的网络,右边是残差网络,较细的线代表验证误差,较粗的线则代表训练误差。我们可以看到普通的网络存在梯度退化的现象,即34层网络的训练和验证误差都大于18层的网络,而残差网络中则不存在这个现象。可见残差网络解决了梯度退化的问题。

为了搭建Resnet网络,我们使用了以下策略:

  • 使用identity_block这个函数来搭建Resnet34,使用bottleneck这个函数来搭建Resnet50。
  • 每个卷积层后都使用BatchNormalization,来防止模型过拟合,并且使输出满足高斯分布。
  • 具体网络搭建可以参考Tabel.1,可以边看表里面的具体参数边搭网络。

整个代码的流程如下:

graph TD
A(导入相应库) --> Z[模型参数设置以及其它配置]
Z --> B[生成训练集,测试集,验证集的三个迭代器] 
B --> C[identity_block函数的编写]
C --> D[bottleneck_block函数的编写]
D --> F[根据resnet网络构建表来构建网络]
F --> G[模型训练与验证]
G --> H[模型保存]
H --> I(模型在测试集上测试)

# coding=utf-8
from keras.models import Model
from keras.layers import Input, Dense, Dropout, BatchNormalization, Conv2D, MaxPooling2D, AveragePooling2D, concatenate, \Activation, ZeroPadding2D
from keras.layers import add, Flatten
from keras.utils import plot_model
from keras.metrics import top_k_categorical_accuracy
from keras.preprocessing.image import ImageDataGenerator
from keras.models import load_model
import os# Global Constants
NB_CLASS=20
IM_WIDTH=224
IM_HEIGHT=224
train_root='/home/faith/keras/dataset/traindata/'
vaildation_root='/home/faith/keras/dataset/vaildationdata/'
test_root='/home/faith/keras/dataset/testdata/'
batch_size=32
EPOCH=60# train data
train_datagen = ImageDataGenerator(width_shift_range=0.1,height_shift_range=0.1,shear_range=0.1,zoom_range=0.1,horizontal_flip=True,rescale=1./255
)
train_generator = train_datagen.flow_from_directory(train_root,target_size=(IM_WIDTH, IM_HEIGHT),batch_size=batch_size,shuffle=True
)# vaild data
vaild_datagen = ImageDataGenerator(width_shift_range=0.1,height_shift_range=0.1,shear_range=0.1,zoom_range=0.1,horizontal_flip=True,rescale=1./255
)
vaild_generator = train_datagen.flow_from_directory(vaildation_root,target_size=(IM_WIDTH, IM_HEIGHT),batch_size=batch_size,
)# test data
test_datagen = ImageDataGenerator(rescale=1./255
)
test_generator = train_datagen.flow_from_directory(test_root,target_size=(IM_WIDTH, IM_HEIGHT),batch_size=batch_size,
)def Conv2d_BN(x, nb_filter, kernel_size, strides=(1, 1), padding='same', name=None):if name is not None:bn_name = name + '_bn'conv_name = name + '_conv'else:bn_name = Noneconv_name = Nonex = Conv2D(nb_filter, kernel_size, padding=padding, strides=strides, activation='relu', name=conv_name)(x)x = BatchNormalization(axis=3, name=bn_name)(x)return xdef identity_Block(inpt, nb_filter, kernel_size, strides=(1, 1), with_conv_shortcut=False):x = Conv2d_BN(inpt, nb_filter=nb_filter, kernel_size=kernel_size, strides=strides, padding='same')x = Conv2d_BN(x, nb_filter=nb_filter, kernel_size=kernel_size, padding='same')if with_conv_shortcut:shortcut = Conv2d_BN(inpt, nb_filter=nb_filter, strides=strides, kernel_size=kernel_size)x = add([x, shortcut])return xelse:x = add([x, inpt])return xdef bottleneck_Block(inpt,nb_filters,strides=(1,1),with_conv_shortcut=False):k1,k2,k3=nb_filtersx = Conv2d_BN(inpt, nb_filter=k1, kernel_size=1, strides=strides, padding='same')x = Conv2d_BN(x, nb_filter=k2, kernel_size=3, padding='same')x = Conv2d_BN(x, nb_filter=k3, kernel_size=1, padding='same')if with_conv_shortcut:shortcut = Conv2d_BN(inpt, nb_filter=k3, strides=strides, kernel_size=1)x = add([x, shortcut])return xelse:x = add([x, inpt])return xdef resnet_34(width,height,channel,classes):inpt = Input(shape=(width, height, channel))x = ZeroPadding2D((3, 3))(inpt)#conv1x = Conv2d_BN(x, nb_filter=64, kernel_size=(7, 7), strides=(2, 2), padding='valid')x = MaxPooling2D(pool_size=(3, 3), strides=(2, 2), padding='same')(x)#conv2_xx = identity_Block(x, nb_filter=64, kernel_size=(3, 3))x = identity_Block(x, nb_filter=64, kernel_size=(3, 3))x = identity_Block(x, nb_filter=64, kernel_size=(3, 3))#conv3_xx = identity_Block(x, nb_filter=128, kernel_size=(3, 3), strides=(2, 2), with_conv_shortcut=True)x = identity_Block(x, nb_filter=128, kernel_size=(3, 3))x = identity_Block(x, nb_filter=128, kernel_size=(3, 3))x = identity_Block(x, nb_filter=128, kernel_size=(3, 3))#conv4_xx = identity_Block(x, nb_filter=256, kernel_size=(3, 3), strides=(2, 2), with_conv_shortcut=True)x = identity_Block(x, nb_filter=256, kernel_size=(3, 3))x = identity_Block(x, nb_filter=256, kernel_size=(3, 3))x = identity_Block(x, nb_filter=256, kernel_size=(3, 3))x = identity_Block(x, nb_filter=256, kernel_size=(3, 3))x = identity_Block(x, nb_filter=256, kernel_size=(3, 3))#conv5_xx = identity_Block(x, nb_filter=512, kernel_size=(3, 3), strides=(2, 2), with_conv_shortcut=True)x = identity_Block(x, nb_filter=512, kernel_size=(3, 3))x = identity_Block(x, nb_filter=512, kernel_size=(3, 3))x = AveragePooling2D(pool_size=(7, 7))(x)x = Flatten()(x)x = Dense(classes, activation='softmax')(x)model = Model(inputs=inpt, outputs=x)return modeldef resnet_50(width,height,channel,classes):inpt = Input(shape=(width, height, channel))x = ZeroPadding2D((3, 3))(inpt)x = Conv2d_BN(x, nb_filter=64, kernel_size=(7, 7), strides=(2, 2), padding='valid')x = MaxPooling2D(pool_size=(3, 3), strides=(2, 2), padding='same')(x)#conv2_xx = bottleneck_Block(x, nb_filters=[64,64,256],strides=(1,1),with_conv_shortcut=True)x = bottleneck_Block(x, nb_filters=[64,64,256])x = bottleneck_Block(x, nb_filters=[64,64,256])#conv3_xx = bottleneck_Block(x, nb_filters=[128, 128, 512],strides=(2,2),with_conv_shortcut=True)x = bottleneck_Block(x, nb_filters=[128, 128, 512])x = bottleneck_Block(x, nb_filters=[128, 128, 512])x = bottleneck_Block(x, nb_filters=[128, 128, 512])#conv4_xx = bottleneck_Block(x, nb_filters=[256, 256, 1024],strides=(2,2),with_conv_shortcut=True)x = bottleneck_Block(x, nb_filters=[256, 256, 1024])x = bottleneck_Block(x, nb_filters=[256, 256, 1024])x = bottleneck_Block(x, nb_filters=[256, 256, 1024])x = bottleneck_Block(x, nb_filters=[256, 256, 1024])x = bottleneck_Block(x, nb_filters=[256, 256, 1024])#conv5_xx = bottleneck_Block(x, nb_filters=[512, 512, 2048], strides=(2, 2), with_conv_shortcut=True)x = bottleneck_Block(x, nb_filters=[512, 512, 2048])x = bottleneck_Block(x, nb_filters=[512, 512, 2048])x = AveragePooling2D(pool_size=(7, 7))(x)x = Flatten()(x)x = Dense(classes, activation='softmax')(x)model = Model(inputs=inpt, outputs=x)return modeldef acc_top2(y_true, y_pred):return top_k_categorical_accuracy(y_true, y_pred, k=2)def check_print():# Create a Keras Modelmodel = resnet_50(IM_WIDTH,IM_HEIGHT,3,NB_CLASS)model.summary()# Save a PNG of the Model Buildplot_model(model, to_file='resnet.png')model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['acc',top_k_categorical_accuracy])print 'Model Compiled'return modelif __name__ == '__main__':if os.path.exists('resnet_50.h5'):model=load_model('resnet_50.h5')else:model=check_print()model.fit_generator(train_generator,validation_data=vaild_generator,epochs=EPOCH,steps_per_epoch=train_generator.n/batch_size,validation_steps=vaild_generator.n/batch_size)model.save('resnet_50.h5')loss,acc,top_acc=model.evaluate_generator(test_generator, steps=test_generator.n / batch_size)print 'Test result:loss:%f,acc:%f,top_acc:%f' % (loss, acc, top_acc)

实验结果

DataLossAccTop5-acc
Training set1.8539.9%85.3%
Vaildation set2.0136.6%82.0%
Testing set2.0835.7%78.1%
DatasetVOC2012Classes20
ModelResNetFrameworkKeras

实验结果分析

我们可以发现模型最后在测试集上的效果与训练集上的效果有一定程度上的差距,模型出现了一点过拟合。为了防止过拟合,而且为了加速收敛,本文在每一层之间都是用了BatchNormalization层。由于本文只训练了60个epoch,每个epoch差不多迭代500次,由于训练的次数太少,故效果并未具体显现。

本博客相关引用

以下是本博客的引用,再次本人对每个引用的作者表示感谢。读者如果对Resnet这个网络仍然存在一些疑虑,或者想要有更深的理解,可以参考以下的引用。

引用博客1

引用博客2

引用文献1:Deep Residual Learning for Image Recognition

引用文献2:Residual Networks are Exponential Ensembles of Relatively Shallow Networks

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

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

相关文章

简易计算器

用JavaScript实现简易计算器 先看效果图 计算加法弹框输出计算结果 程序解读&#xff1a; 1.两个输入框和一个下拉框 2. 弹框显示结果 3. 加减乘除的简单运算 代码演示 Html页面内容 <!DOCTYPE html> <html><head><meta charset"utf-8">…

【OpenCV 例程300篇】50. 直方图处理之直方图统计量图像增强

『youcans 的 OpenCV 例程300篇 - 总目录』 【youcans 的 OpenCV 例程300篇】50. 直方图处理之直方图统计量图像增强 图像直方图是反映图像像素分布的统计表。 灰度直方图是图像灰度级的函数&#xff0c;用来描述每个灰度级在图像矩阵中的像素个数。 直方图统计量图像增强&…

在大数据时代下金融风控的分类

Date&#xff1a;2018-05-24 Author&#xff1a;等等 依托城市数据湖海量数据资源&#xff0c;尤其是在信贷领域对企业或者个人的个人信贷画像描述评判准则已经是第三方房贷企业或者银行对借贷人的评分标准。风控建模以数据价值挖掘为导向&#xff0c;吸纳大量数据、人工智能科…

多功能标准型计算器

实现一个标准型计算器及其各项功能的实现 效果图欣赏 是不是看起来很漂亮的呢&#xff1f;&#xff1f;&#xff1f; 功能详解&#xff1a; 屏幕显示输入的数字和符号实现加减乘除运算回退和清零功能小数的运算结果的输出 相信小伙伴们都已经迫不及待的想要知道源码了。 代…

【OpenCV 例程200篇】51. 直方图处理之直方图反向追踪(cv2.calcBackProject)

『youcans 的 OpenCV 例程300篇 - 总目录』 【OpenCV 例程200篇】51. 直方图处理之直方图反向追踪&#xff08;cv2.calcBackProject&#xff09; 图像直方图是反映图像像素分布的统计表。 灰度直方图是图像灰度级的函数&#xff0c;用来描述每个灰度级在图像矩阵中的像素个数。…

【转需】【金融干货】四步教你:开发风控模型?

一、市场调研 目前市面主流的风控模型 1、互联网金融前10名排行榜(数据截止日期2017-09-12) 互联网金融公司排名分别是蚂蚁金服、陆金所、京东金融、苏宁金融、百度金融、腾讯理财通、宜信、钱大掌柜、万达金融和网易理财。 1.1 蚂蚁金服 1.1.1 大数据技术对接第三方征信公司芝…

推箱子

推箱子小游戏原理的实现 功能分析&#xff1a; 1.找一张类似于箱子的图片自行设置图片的大小 2.设置控制箱子移动的四个方向的按钮 3.使用键盘控制图片上下左右的移动、 效果图演示 原始位置 点击键盘向右移动三次向下移动一次后的位置 看了上述过程是不是感觉很有意思呀&…

我的Go+语言初体验——(5)Go+ 基本语法之 Switch

我的Go语言初体验——&#xff08;5&#xff09;Go 基本语法之 Switch “我的Go语言初体验” | 征文活动进行中… Go 语言中提供多路分支条件语句 switch&#xff0c; 用于在不同条件下执行不同动作。 使用 if-else 嵌套结构也可以实现多路分支条件结构&#xff0c;但程序冗长…

java web 开发之写在前面(0)

java是sun公司&#xff08;现在属于Oracle公司&#xff09;推出的能够跨越多平台的、可以执行最高的一种面向对象的编程语言&#xff0c;也是目前最先进、特征最丰富、功能最强大的计算机语言。利用java可以编写桌面应用程序&#xff0c;web应用程序、分布式系统、嵌入式系统程…

【OpenCV 例程200篇】52. 图像的相关与卷积运算

【OpenCV 例程200篇】52. 图像的相关与卷积运算 欢迎关注 『OpenCV 例程200篇』 系列&#xff0c;持续更新中 欢迎关注 『Python小白的OpenCV学习课』 系列&#xff0c;持续更新中 滤波通常是指对图像中特定频率的分量进行过滤或抑制。图像滤波是在尽可能保留图像细节特征的条件…

java web 之 网页前端开发基础(1)

1.HTML&#xff08;Hypertext Markup Language&#xff0c;HTML&#xff0c;超文本标记语言&#xff09; 1.1 创建第一个HTML文件 编写html语言可以通过两种方式&#xff0c;一种是手工编写html代码&#xff0c;一种是借助一些开发软件&#xff0c;如Dreamweaver或者微软公司…

文本框为空按钮不可点击

在form表单的提交中判断输入框的内容是否为空&#xff0c;如果输入框的内容为空则按钮不可点击&#xff0c;只有当输入框的内容不为空时才能点击并执行之后的提交等操作。 效果图演示 输入框为空&#xff08;按钮不可点击&#xff0c;点击无效果&#xff09; 输入框不为空时…

【youcans 的 OpenCV 学习课】7. 空间域图像滤波

专栏地址&#xff1a;『youcans 的图像处理学习课』 文章目录&#xff1a;『youcans 的图像处理学习课 - 总目录』 【youcans 的 OpenCV 学习课】7. 空间域图像滤波 图像滤波是在尽可能保留图像细节特征的条件下对目标图像的噪声进行抑制&#xff0c;是常用的图像预处理操作。 …

java web开发之上机指导(2)

创建一个用户注册的页面&#xff0c;让用户输入姓名、密码、电话和邮箱&#xff0c;使用javascript脚本完成密码校验、电话号码校验、邮箱校验和空格内容校验。 开发步骤如下。 &#xff08;1&#xff09;创建一个项目名为CheckInfomation&#xff0c;在WebContent文件夹下创…

小程序开发之基础知识(0)

前言&#xff1a;2016年9月21日,微信小程序正式开启内测。 2017年1月9日0点,万众瞩目的微信第一批小程序正式上线,用户可以体验到各种各样小程序提供的服务。 人类发展史大抵经历了石器时代&#xff0c;青铜器与铁器时代、工业与科技时代&#xff08;从第一次工业革命算起&…

鼠标悬浮改变背景颜色

将鼠标放在div上时div的背景颜色发生改变&#xff0c;把鼠标移走div之后&#xff0c;div的背景颜色回复原来的颜色。 效果图演示 没有将鼠标放在div上时&#xff08;lanse&#xff09; 将鼠标放在div上之后&#xff08;变为红色&#xff09; 下面看代码 由于代码较短&…

我的Go+语言初体验——(6)整型有理数数据类型

我的Go语言初体验——&#xff08;6&#xff09;整型有理数数据类型 “我的Go语言初体验” | 征文活动进行中… Go 语言使用后缀 ‘r’ 表示有理数&#xff0c;支持整型、分数型、浮点型三种有理数数据类型&#xff08;Rational number&#xff09;。 在整型有理数变量声明时&…

利用python进行数据分析之准备工作(1)

目录 一、简介 二、重要的python库 1.numpy库 2.pandas 3.matplotlib 4.IPython 5.Scipy 三、python环境安装和数据分析前的数据准备 一、简介 什么是数据&#xff1f;本栏目的数据主要指的是结构化的数据&#xff0c;通常我们使用数据这一说法来笼统地概括所有通用格式…

获取焦点改变输入框背景色

当输入框获取焦点时&#xff08;鼠标点到输入框时&#xff09;为红色&#xff0c;失去焦点时还原&#xff08;鼠标未点输入框内&#xff09;为白色。 效果图演示 没有获取焦点 获取焦点 代码演示 <!DOCTYPE html> <html><head lang"en"><…

【OpenCV 例程200篇】53. Scipy 实现图像二维卷积

【OpenCV 例程200篇】53. Scipy 实现图像二维卷积 欢迎关注 『OpenCV 例程200篇』 系列&#xff0c;持续更新中 欢迎关注 『Python小白的OpenCV学习课』 系列&#xff0c;持续更新中 滤波通常是指对图像中特定频率的分量进行过滤或抑制。图像滤波是在尽可能保留图像细节特征的条…