位置: IT常识 - 正文

机器学习——图像分类

编辑:rootadmin
机器学习——图像分类 1 图像分类的概念1.1 什么是图像分类?

推荐整理分享机器学习——图像分类,希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:,内容如对您有帮助,希望把文章链接给更多的朋友!

图像分类,根据图像信息中所反映出来的不同特征,把不同类别的目标区分开来的图像处理方法

1.2 图像分类的难度

●任何拍摄情 况的改变都将提升分类的难度

 1.3 CNN如何进行图像分类

        ●数据驱动型方法通用流程                 1.收集图像以及对应的标签,形成数据集                2.使用机器学习训练一个分类器                 3.在新的图像.上测试这个分类器

 ​​​

 1.4 图像分类指标

精确率:查得准不准?         召回率:查得全不全?        

 True positives (TP):飞机的图片被正确的识别成了飞机。 True negatives (TN): 大雁的图片没有被识别出来,系统正确地认为它们是大雁。 False positives (FP):大雁的图片被错误地识别成了飞机。 False negatives (FN):飞机的图片没有被识别出来,系统错误地认为它们是大雁。

 True negatives (TN): 4,四个大雁                        False negatives (FN): 2,二个飞机

True positives (TP): 3,绿框.                                False positives (FP): 1,红框,

        

准确率:        

平均精确度( Average Precision,AP) :PR曲线下的面积,这里的average,等于是对precision进行取平均。

 1.5 经典CNN网络性能演化2 GoogleNet   2.1 深度网络有什么好处?

1.丰富了低、中、高等级的特征 边缘、纹理、形状、颜色.....高纬度的人类无法理解的特征 2.越深、越宽的网络具有越强的表达能力

日有学者证明,一个宽度为K、深度为H的网络,能够产生至少条线段线段越多,拟合得越准确因此,网络加宽、加深可以提升性能,并且加深效果比加宽好:2.2 如何设计一个卷积层?

        ●选择什么样的层(Layer ) ?                 3x3卷积核                 5X5卷积核                 池化层( Pooling Layer )

2.2.1 感受层

         在卷积神经网络中,感受野( Receptive Field )的定义是卷积神经网络每- -层输出的特征图.上的像素点在输入图片.上映射的区域大小。换句话说,感受野是特征图上的一个点对应输入图上的区域。

假设两个卷积层的卷积核尺寸都为2x2,步长都为1,输入为4x4经过两次卷积后,特征图的尺寸分别为3x3和2x2对于特征图2的左上角像素点,它在特征图1上的感受范围为左上方的2x2区域,而此区域在输入。上的感受范围是左,上方的3x3区域,因此,感受野尺寸为3x3。  

  日越深层的特征图, 感受野越大对同层而言,卷积核尺寸越大,感受野越大大的感受野对大的物体更敏感,反之,小的感受野对小的物体更敏感

 猫可以在图片里有大有小,可以在图片的局部,也可以整张图片都是;对一张图片而言,至少有RGB三个通道,如果这几多个卷积核则会导致计算量过大。 

机器学习——图像分类

2.2.2 如何降低计算量——1x1卷积核

1 x 1卷积做了什么?

它在深度( Depth). 上进行了融合深度为D的输入经过一-个1 x 1卷积核,得到深度为1的输出(S=1, P=0);同理,尺寸为DxHxW的输入,经过D/2个1 X 1卷积核,将会得到D/2xHxW的输出(S=1,P=0);最终,在不损失太多信息的情况下,对输入进行了降维。

 小结:1X1的卷积是--个非常优秀的结构它可以跨通道组织信息提高网络的表达能力,同时可以对输出通道升维和降维。[想象一下:两片面包压缩成一-片的宽度又或者加点膨化剂,膨胀成4片的宽度]

2.3 Inception模块

在1x1卷积后,添加不同的卷积分支实现同一卷积层的多尺度特征提取与融合2.4 整体网络结构

 一个潜在的问题

        ➢在较深的网络中进行反向传播可能会出现“梯度消失”,导致训练无法继续进行

一种解决方案

        ➢网络的中间层具有很高的判别能力         ➢在这些中间层增加辅助分类器         ➢在训练中,这些中间层分类器得到的L .oss以0.3的权重加到最终Loss

3 GoogleNet的keras实现

3.0 猫狗大战

本次实战采用的数据集来自kaggle . 上的一一个竞赛: Dogs Vs. Cats

 

 3.1 图像读取一图像增 强-图像生成器

 数据增强策略         ●翻转变换(lip):沿着水平或者垂直方向翻转图像;         ●缩放变换(zoom):按照一定的比例放大或者缩小图像;         ●平移变换(shift):在图像平面上对图像以一定方式进行平移;         ●可以采用随机或人为定义的方式指定平移范围和平移步长,沿水平或竖直方向进行平移.图像内容的位置         ●尺度变换(scale):对图像按照指定的尺度因子,进行放大或缩小;或者参照SIFT特征提取思想,利用指定的尺度因子对图像滤波构造尺度空间.改变图像内容的大小或模糊程度;         ●对比度变换(contrast):在图像的HSV颜色空间,改变饱和度S和V亮度分量,保持色调H不变.对每个像素的S和V分量进行指数运算(指数因子在0.25到4之间),增加光照变化;         ●噪声扰动(noise):对图像的每个像素RGB进行随机扰动,常用的噪声模式是椒盐噪声和高斯噪声;

代码如下:

import numpy as npfrom keras.preprocessing.image import ImageDataGeneratorfrom PIL import Imageimport matplotlib.pyplot as plt%matplotlib inlinetrain_dir="train" #训练集路径test_dir="test"#测试集路径IM_WIDTH=224 #图像宽度IM_HEIGHT=224 #图像高度batch_size=32#定义训练和测试的图像生成器#train and val datatrain_val_datagen = ImageDataGenerator (rotation_range=30, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, z0om_range=0.2, horizontal_flip=True, validation__split=0.1) #划分出验证集#test datatest_datagen=ImageDataGenerator() #测试集就不用数据增强了#训练集图像生成器train_generator=train_val_datagen.flow_from_directory(train_dir, target_size=(IM_WIDTH,IM_HEIGHT), #将目标图片缩放多大的尺寸 batch_size=batch_size, #分批次抽取 subset='training')#验证集图像生成器vaild_generator=train_val_datagen.flow_from_directory(train_dir, target_size=(IM_WIDTH,IM_HEIGHT), batch_size=batch_size, subset='validation')#测试集图像生成器test_generator=test_datagen.flow_from_directory(test_dir, target__size=(IM_WIDTH,IM_HEIGHT), batch_size=batch_size,)#验证图片生成器的效果,选取生成器的下一个图片并打印出来samples_batch=train_generator.next()print(samples_batch[0].shape)#第0位保存的是图像print(samples_batch[1].shape)#第1位保存的是标签#显示一张图片fig1=samples_batch[0][0]r=Image.fromarray(fig1[:,:,0]).convert('L')#读第0个通道内的值,转为灰度值g=Image.fromarray(fig1[:,:,1]).convert('L')b=Image.fromarray(fig1[:,:,2]).convert('L')image=Image.merge("RGB",(r,g,b))#RGB合并起来plt.imshow(image)plt.show()print(samples_batch[1][0])#打印标签——热编码

运行结果:

3.2 自定义图像生成器

#自定义训练集生成器def myTrainDataGenerator(): while True: trainDataBatch=train_generator.next() #取出一个批次的数据 images=trainDataBatch[0] #取图像 labels= [trainDataBatch[1] , trainDataBatch[1] , trainDataBatch[1]]#取标签 yield images, labels#自定义验证集生成器def myVaildDataGenerator(): while True: vaildDataBatch=vaild_generator.next() #取出一个批次的数据 images=vaildDataBatch[0] #取图像 labels= [vaildDataBatch[1] , vaildDataBatch[1] , vaildDataBatch[1]]#取标签 yield images, labels#自定义测试集生成器def myTestDataGenerator(): while True: testDataBatch=test_generator.next() #取出一个批次的数据 images=testDataBatch[0] #取图像 labels= [testDataBatch[1] , testDataBatch[1] , testDataBatch[1]]#取标签 yield images, labelsmy_train_generator=myTrainDataGenerator()my_vaild_generator=myVaildDataGenerator()my_test_generator=myTestDataGenerator()a=my_train_generator.__next__()#显示一张图片fig1=a[0][0]r=Image.fromarray(fig1[:,:,0]).convert('L')#读第0个通道内的值,转为灰度值g=Image.fromarray(fig1[:,:,1]).convert('L')b=Image.fromarray(fig1[:,:,2]).convert('L')image=Image.merge("RGB",(r,g,b))#RGB合并起来plt.imshow(image)plt.show()print(samples_batch[1][0][0])

3.3 模型实现

#导入需要使用的包from keras.models import Modelfrom keras.layers import Input , Dense, Dropout , BatchNormalization, Conv2D , MaxPool2D , AveragePooling2D, concatenate, Flattenfrom keras.layers.convolutional import Conv2D ,MaxPooling2D, AveragePooling2Dfrom keras.callbacks import ReduceLROnPlateau,ModelCheckpoint , EarlyStoppingfrom keras.preprocessing.image import ImageDataGeneratorfrom keras.models import load_modelfrom keras.preprocessing.image import ImageDataGeneratorfrom PIL import Image#若需要复现,可以把随机数固定下来seed=42np.random.seed(seed)#卷积+BNdef Conv2d_BN(prev_layer, #卷积前一层网络 filters, #卷积核数量 kernel_size, #卷积核大小 padding='same', #‘sanme'指卷积填充是大小保持不小 strides=(1,1) , #步长 name=None #名字 ): if name is not None: bn_name = name+'_bn' conv_name = name +'_conv' else: bn_name = None conv_name = None x = Conv2D(filters, kernel_size, padding=padding,strides=strides , activation='relu',name=conv_name) (prev_layer) #2D图像卷积参数 x = BatchNormalization(axis=3, name=bn_name)(x) #批次归一化,- -种标准化操作,防止过拟合的手段 return x#inception模块def inception_block(prev_layer, num_filters, name, use_whistle = False, numclasses = -1):#num_filters: [b0,(b11, b12)。(b21,b22)。 b3] 代表不同分支的通道数,即卷积核个数#use_ whistle:是否要输出辅助分类器 #1x1卷积分支 branch0=Conv2d_BN(prev_layer=prev_layer,filters=num_filters[0],kernel_size=(1,1),name=name+'-br0-1x1') #3x3卷积分支,1x1-3x3 branch1=Conv2d_BN(prev_layer=prev_layer,filters=num_filters[1][0],kernel__size=(1,1),name=name+'-br1-1x1') branch1=Conv2d_BN(prev__layer=branch1,filters=num_filters[1][1],kernel_size=(3,3),name=name+'-br1-3x3') #5x5卷积分支,1x1-5x5 branch2=Conv2d_BN(prev__layer=prev_layer,filters=num_filters[2][0],kernel_size=(1,1),name=name+'-br2-1x1') branch2=Conv2d_BN(prev_layer=branch2,filters=num_filters[2][1],kernelsize=(5,5),name=name+'-br2-5x5') #池化分支 branch3=MaxPool2D(pool_size=(3,3),strides=(1,1),padding='same',name=name+'-br3-pooL')(prev_layer) branch3=Conv2d_BN(branch3,filters=num_filters[3],kernel__size=(1,1),name=name+'-br3-1x1') #融合 x = concatenate([branch0, branch1, branch2, branch3], axis = 3,name = name) #是否输出辅助分类器 if(use_whistle): out = aux_whistle(prev_layer, numclasses = numclasses, name = name + '-whistle') return x,out return x#辅助分类器def aux_whistle(prev_layer,numclasses,name): aux_clf=AveragePooling2D(pool_size=(5,5),strides=(3,3),name=name+'-averagePool')(prev_layer) #池化 aux_clf=Conv2d_BN(aux_clf,filters=128,kernel__size=(1,1),name=name+'-1x1conv') #卷积 aux_clf=Flatten(name=name+'-flatten')(aux_clf) aux_clf=Dense(1024,activation='relu')(aux_clf) #全连接 aux_clf=Dropout(0.3,name=name+'-dropout')(aux_clf) aux_clf=Dense(num_classes,activation='softmax',name=name+'-predictions')(aux_clf) return aux_clfdef inceptionNet(input_shape,numclasses): inp=Input(shape=input_shape) #「卷积+池化」x2 x=Conv2d_BN(inp,filters=64,kernel_size=(7,7),strides=(2,2),name='2a') x=MaxPool2D(poolsize=(3,3),strides=(2,2),padding='same',name='2pool-1')(x) x=Conv2d_BN(x,filters=192,kernel_size=(3,3),name='2b') x=MaxPool2D(pool__size=(3,3),strides=(2,2),padding='same',name='2pool-2')(x) #第-Inception模块组,3a.3b x=inception_block(x,(64,(96,128),(16,32),32),name='inception3a') x=inception_block(x,(128,(128,192),(32,96),64),name='inception3b') x=MaxPoo12D(pool_size=(3,3),strides=(2,2),padding='same',name='3pool')(x) #第二Inception模块组,4a、4b(辅助)、4c.4d(辅助)、4e x=inception_block(x,(192,(96,208),(16,48),64),name='inception4a') x,whistle1=inception_block(x,(160,(112,224),(24,64),64),name='inception4b',use_whistle=True,numclasses=numclasses) x=inception_block(x,(128,(128,256),(24,64),64),name='inception4c') x,whistle2=inception_block(x,(112,(144,288),(32,64),64),name='inception4d',use_whistle=True,numclasses=numclasses) x=inception_block(x,(256,(160,320),(32,128),128),name='inception4e') x=MaxPool2D(poolsize=(3,3),strides=(2,2),padding='same',name='4pool')(x) #第三Inception模块组,5a.5b x=inception_block(x,(256,(160,320),(32,128),128),name='inception5a') x=inception_block(x,(384,(192,384),(48,128),128),name='inception5b') #全局平均池化 x=AveragePooling2D(pool_size=(7,7),strides=(1,1),padding='valid',name='avg7x7')(x) #X=Dropout(0.4)(x) # FC+Softmax分类 x = Flatten (name='flatten')(x) x = Dense(numclasses, activation= 'softmax',name= 'predictions')(x) model = Model( inp, [x, whistle1,whistle2] ,name=' inception_v1') return model

3.4 模型编译

num_classes=len(train_generator.classindices) #获取类别数model=inceptionNet(input_shape=(224,224,3),numclasses=num_classes)#获取model对象model.compile(optimizer='adam', #优化器 loss='categorical_crossentropy', #损失函数 loss_weights=[1.0,0.3,0.3], #损失函数权重 metrics=['accuracy']) #评价标准(错误率),如果要用top-k:['accuracy',metric.top_k__categorical_accuracy]model.summary()#打印出模型概述信息

3.5 模型训练

EPOCH=10 #一个Epoch代表遍历- -次所有数据batch_size=32 #一 个批次内的图片数量modelfilepath='model.best.hdf5' #保存路劲#无法更优则自动终止earlyStop=EarlyStopping(monitor='val_predictions__acc', patience=30, verbose=1, mode='auto')#保存最好的模型checkpoint=ModelCheckpoint(modelfilepath, monitor='val_predictions__acc', verbose=1, save__best_only=True, mode='max')#根据不同阶段,降低学习率reduce_Ir=ReduceLROnPlateau(monitor='val_predictions_loss', factor=0.1, patience=10, verbose=1, mode='auto', min_delta=0.00001, C0oldown=0, min__lr=0)history=model.fit_generator(my_train_generator,validation_data=my_vaild_generator,epochs=EPOCH,steps_per_epoch=train_generator.n/batch_size,validation_steps=vaild_generator.n/batch_size,callbacks=[checkpoint,reduce_lr,earlyStop])

训练结果:

3.6 模型测试

#=====模型测试=========testmodel=load_model (modelfilepath)loss,predictions_loss,aux1_loss, aux2_loss, predictions_acc,aux1_acc, aux2_acc=testmodel. evaluate_generator(my_test_generator,steps=test_generator.n/batch_size)#绘制训练&验证的准确率值plt.plot(history.history['predictions_acc'])plt.plot(history.history['val_predictions_acc'])plt.title('Model accuracy')plt.ylabel('Accuracy')plt.xlabel('Epoch')plt.legend(['Train','Val'],loc='upper left')plt.show()#绘制训练&验证的损失值plt.plot(history.history['loss'])plt.plot(history.history['val_loss'])plt.title('Model loss')plt.ylabel('Loss')plt.xlabel('Epoch')plt.legend(['Train','Val'],loc='upper left')plt.show()

测试结果:

本文链接地址:https://www.jiuchutong.com/zhishi/299367.html 转载请保留说明!

上一篇:KITTI数据集解析和可视化(timit数据集)

下一篇:毕业设计-基于 MATLAB 的车牌识别系统设计(毕业设计教程)

  • stkaloo华为是什么型号

    stkaloo华为是什么型号

  • 正在检查磁盘错误,这可能要花费一个多小时怎么回事(正在检查磁盘错误一个多小时)

    正在检查磁盘错误,这可能要花费一个多小时怎么回事(正在检查磁盘错误一个多小时)

  • 微信置顶聊天怎么设置(微信置顶聊天怎么折叠不了)

    微信置顶聊天怎么设置(微信置顶聊天怎么折叠不了)

  • 电脑管家跑分多少算好

    电脑管家跑分多少算好

  • oppo怎么把字体变大(oppo怎么把字体调大)

    oppo怎么把字体变大(oppo怎么把字体调大)

  • 抖音怎样不显示在线(抖音怎样不显示特效)

    抖音怎样不显示在线(抖音怎样不显示特效)

  • 1tb hdd是什么硬盘(1tb hdd,128gb ssd)

    1tb hdd是什么硬盘(1tb hdd,128gb ssd)

  • 蓝牙耳机充电仓怎么修(蓝牙耳机充电仓丢了怎么办)

    蓝牙耳机充电仓怎么修(蓝牙耳机充电仓丢了怎么办)

  • 微信注销之后手机号还能注册吗(微信注销之后手机号就解绑了吗)

    微信注销之后手机号还能注册吗(微信注销之后手机号就解绑了吗)

  • 手机微信没声音了如何恢复(手机微信没声音怎么调)

    手机微信没声音了如何恢复(手机微信没声音怎么调)

  • 手机插到电脑上后只充电识别不到(手机插到电脑上会留下数据吗)

    手机插到电脑上后只充电识别不到(手机插到电脑上会留下数据吗)

  • qq自动回复算不算火花(qq自动回复算不算回复)

    qq自动回复算不算火花(qq自动回复算不算回复)

  • 幻灯片比例在哪里设置(幻灯片比例在哪里调整)

    幻灯片比例在哪里设置(幻灯片比例在哪里调整)

  • 为什么视频发不出(为什么视频发不到朋友圈里去?)

    为什么视频发不出(为什么视频发不到朋友圈里去?)

  • 硬盘与内存的区别(硬盘与内存的区别与联系)

    硬盘与内存的区别(硬盘与内存的区别与联系)

  • word化学式下标怎么打(word中化学式右下角数)

    word化学式下标怎么打(word中化学式右下角数)

  • 为什么小爱音响连不上蓝牙(为什么小爱音响连蓝牙声音小)

    为什么小爱音响连不上蓝牙(为什么小爱音响连蓝牙声音小)

  • vue怎么弄慢镜头(vue怎么倍速)

    vue怎么弄慢镜头(vue怎么倍速)

  • 微博我的访问记录怎么删除不了(微博我的访问记录仅自己可见什么意思)

    微博我的访问记录怎么删除不了(微博我的访问记录仅自己可见什么意思)

  • 机械表跳日期都不准吗(机械表日期跳一半怎么回事)

    机械表跳日期都不准吗(机械表日期跳一半怎么回事)

  • 手机6英寸是多少厘米(手机6英寸是多长多宽)

    手机6英寸是多少厘米(手机6英寸是多长多宽)

  • 快手相机胶卷怎么删除(快手相机胶卷怎么弄)

    快手相机胶卷怎么删除(快手相机胶卷怎么弄)

  • 怎么看qq绑定的身份证(怎么看qq绑定的第三方软件)

    怎么看qq绑定的身份证(怎么看qq绑定的第三方软件)

  • kkbox大陆能用吗(kkbox好用吗)

    kkbox大陆能用吗(kkbox好用吗)

  • 朋友圈怎么举报(开车拍视频发朋友圈怎么举报)

    朋友圈怎么举报(开车拍视频发朋友圈怎么举报)

  • 小米手环3恢复出厂设置怎么操作(小米手环3恢复出厂设置步骤)

    小米手环3恢复出厂设置怎么操作(小米手环3恢复出厂设置步骤)

  • qq昵称旁边的小耳朵(qq昵称旁边的小船)

    qq昵称旁边的小耳朵(qq昵称旁边的小船)

  • 微信如何关闭小视频(微信如何关闭小程序音乐功能)

    微信如何关闭小视频(微信如何关闭小程序音乐功能)

  • 苹果MAC OS X Yosemite系统 蓝牙连接出现延迟问题(苹果mac os x 怎样打开DVD播放程序)

    苹果MAC OS X Yosemite系统 蓝牙连接出现延迟问题(苹果mac os x 怎样打开DVD播放程序)

  • Win7系统鼠标键盘驱动检测不到怎么办?Win7检测不到鼠标键盘驱动的方法汇总(win7系统鼠标键盘)

    Win7系统鼠标键盘驱动检测不到怎么办?Win7检测不到鼠标键盘驱动的方法汇总(win7系统鼠标键盘)

  • 广告公司的税务
  • 税后净营业利润英文
  • 汽车运输企业付给车主的运费算成本吗
  • 摊余成本的构成内容
  • 餐饮业增值税是多少
  • 航空公司开具的个人抬头的发票可以税前扣除吗
  • 会计为什么要计折旧费
  • 合伙开有限公司注销流程
  • 住宿发票税点为什么有6%和3%的区别
  • 特定业务预缴所得税不能填写
  • 企业所得税申报时间
  • 管理费用月末结转总账登记图
  • 定额备用金补付会计分录怎么写?
  • 亏损企业所得税弥补
  • 注册资本未到位转让股权
  • 工程维修费用怎么入账
  • 小微企业月销售额不超过15万
  • 前期差错更正处理方法
  • 子公司具有独立的法律地位吗
  • 生产企业出口货物收不到款怎么处理
  • 一月份开的票可以算到12月吗
  • 公司非本单位退休人员发放工资
  • 农村的生产经营是什么
  • 公司出租商铺要交哪些税
  • 固定资产入账包括税额吗
  • 小微企业关闭
  • win10为什么毛病这么多
  • 没有收入该怎么办
  • 优先股分类为金融负债
  • bios设置内存频率后黑屏
  • 限定性净资产要算在现金流量表里吗
  • PHP:preg_replace_callback()的用法_PCRE正则函数
  • 跨年冲减无发票怎么入账
  • php getheader
  • 哪种审计证据可以查出来
  • 金税盘白盘怎么分发发票
  • mysql5.7设置编码
  • 网络贷款需要缴费吗
  • 投资收益收到的现金增加的原因
  • 增值税专用发票查询系统官方网站
  • MySQL/Postgrsql 详细讲解如何用ODBC接口访问MySQL指南
  • mysql 随机取出一条
  • 收到银行开的手续费发票如何做帐
  • 电梯安装费如何分摊
  • 应交增值税的结转怎么算
  • 房地产企业账务处理所用科目
  • 股权转让印花税减半征收政策
  • 小规模纳税人缴税的分录
  • 新公司初期费用全部入哪里
  • 验资账户需要对账吗
  • 税率计税依据
  • a公司刚刚执行了一个采购项目
  • 新建的企业
  • win7怎么删除桌面图标
  • vcspawn.exe什么意思
  • win7偶尔黑屏
  • win8怎么装系统
  • mac book视频
  • linux安装atop
  • win10界面虚化模糊
  • opengl 编程
  • jquery提交表单调用serialize方法
  • 提高网站性能
  • unity的monodevelop
  • jQuery+JSON实现AJAX二级联动实例分析
  • 查看node
  • jquery22插件网
  • jquery实现输入框自限制
  • js中splice方法
  • unity image fillamount
  • $jquery
  • google it automation with python
  • 福建省国税局电话号码
  • 增值税消费税申报比对不符怎么办
  • 贵州税务举报
  • 小程序河南税务局
  • 建筑企业异地预缴增值税计算
  • 车辆购置税非本人可以代缴吗
  • 办税服务厅是税收工作的
  • 个人所得税税收完税证明哪里打印
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设