1.前言
这一次是分享一次我的作业,然后这个是我做的,等老师过几天分享他的之后我会把老师做得好的地方过来更新的。
2.正文
作业背景及要求
还是之前的数据分析与挖掘实战里面的案例,得到的数据集是图片,然后我们需要进行提取图片的特征颜色矩然后建模,大概的流程和操作方法见下图。
基本流程
图像切割
颜色矩的计算公式
开始动手
1.首先,进行图片处理得到颜色矩
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
import os
plt.rcParams['font.family']='FangSong'
plt.rcParams['axes.unicode_minus']=False
#加载数据,看看目录下的图片
imgFile='./images/1_1.jpg'
img=Image.open(imgFile)
print("图片大小为:",img.size)
plt.figure(figsize=(10,8))
plt.imshow(img)
plt.title("水色样本:"+imgFile+"类别标签:"+str(imgFile[9]))
#不显示坐标轴
plt.axis('off')
plt.show()
# 开始对图像的颜色矩特征进行提取
# 通过对数据图像的观察,决定选取每张图像中心处的图像进行颜色矩特征提取
def img_extract():
input_path='./images'
output_path='./result.csv'
result=[]
imglist=os.listdir(input_path)
#print(imglist) 得到类似于1_1.jpg这样的文件名,其中1_1表示属于1类别的第一张图片
for i in range(len(imglist)):
#开始把类别和第几张图片分开
num=imglist[i].rstrip('.jpg').split('_')
#print(num) 得到['1','1']这样的列表
#把字符串列表转换为数值型
num=[int(x) for x in num]
#开始图像分割
img=Image.open(input_path+'/'+imglist[i])
h,w=img.size
#取图片中心100*100的图像
#关于crop的介绍:https://blog.csdn.net/banxia1995/article/details/85330212
box=[h/2-50,w/2-50,h/2+50,w/2+50]
small=img.crop(box)
#提取颜色特征
rgb=np.array(small)/[255.0,255.0,255.0]
#print(rgb)
#一阶颜色矩
first_order=1.0*(rgb.sum(axis=0).sum(axis=0))/10000
err=rgb-first_order
#print(first_order)
#二阶颜色矩
second_order=np.sqrt(1.0*(np.power(err,2)).sum(axis=0).sum(axis=0)/10000)
#三阶颜色矩
third_order=1.0*(pow(err,3).sum(axis=0).sum(axis=0))/10000
third_order=np.cbrt(abs(third_order))*-1.0
#print(third_order)
res=np.concatenate((num,first_order,second_order,third_order))
result.append(res)
#保存到csv文件G
names=['水质类别','序号','R通道一阶矩','G通道一阶矩','B通道一阶矩',
'R通道二阶矩','G通道二阶矩','B通道二阶矩',
'R通道三阶矩','G通道三阶矩','B通道三阶矩']
df=pd.DataFrame(result,columns=names)
#print(df)
df.to_csv(output_path,encoding='utf-8',index=False)
img_extract()
使用img_extract()得到保存有数据的csv文件,然后查看一下数据
因为这里已经有了老师ppt里面给出的处理好的数据,所以来对比一下
发现值差不多所以就直接进行数据建模,在真实情况下我们一般是没有标准数据去对比的,所以这个时候就需要自己去可视化我们得到的数据,看看得到的数据与对应的分类之间有没有明显的关联性或者有没有离群点的错误数据,如果有有可能就是我们的处理提取过程存在问题,这个是不能忽视的!!!
2.建模过程
首先是用要求的SVM来建模,我先尝试没有设置超参数的
导入数据并进行训练集测试集划分
# 开始导入我们做好的特征数据集,进行数据集划分并建模
from sklearn.model_selection import train_test_split
from sklearn import svm
from sklearn import metrics
data=pd.read_csv('./result.csv',encoding='utf-8')
data.head()
#因为数据很小,为了增大数据区分度所以对X乘以30,避免过拟合
X=data.iloc[:,2:]*30
Y=data['水质类别']
Y=Y.astype(int)
X_train,X_test,y_train,y_test=train_test_split(X,Y,test_size=0.3,random_state=2)
print(X_train.shape,X_test.shape)
没有超参数的SVM效果
#先不加任何参数来看看模型效果
model=svm.SVC()
model.fit(X_train,y_train)
train_pred=model.predict(X_train)
test_pred=model.predict(X_test)
print('----------模型在训练集的结果-------')
train_acc=metrics.accuracy_score(y_train,train_pred)
train_cm=metrics.confusion_matrix(y_train,train_pred)
train_report=metrics.classification_report(y_train,train_pred)
print("训练集准确率为:",train_acc)
print("-----------------")
print("训练集混淆矩阵:",train_cm)
print("-----------------")
print("训练集分类报告:",train_report)
print('----------模型在测试集的结果-------')
test_acc=metrics.accuracy_score(y_test,test_pred)
test_cm=metrics.confusion_matrix(y_test,test_pred)
test_report=metrics.classification_report(y_test,test_pred)
print("测试集准确率为:",test_acc)
print("-----------------")
print("测试集混淆矩阵:",test_cm)
print("-----------------")
print("测试集分类报告:",test_report)
没有设置超参数的模型效果其实并不好,但是我看到很多网上博客没有设置超参数就有97%的准确率了,而且数据都是几乎一样的,可能就是因为一些小的数据差别吧,毕竟支持向量选取对模型效果影响还是比较大的。
使用网格搜索调整超参数
#混淆矩阵可视化
import seaborn as sns
def plot_confusion_matrix(confusion_mat):
df_cm = pd.DataFrame(confusion_mat)
ax = sns.heatmap(df_cm,annot=True,fmt='.20g')
ax.set_title('混淆矩阵')
ax.set_xlabel('预测标签')
ax.set_ylabel('真实标签')
plt.show()
#发现模型性能其实并不好,开始试试网格搜索进行超参数优化
from sklearn.model_selection import GridSearchCV
parameters =[{'kernel': ['linear','rbf','poly'],'C': [1, 10, 100, 1000,1200],'gamma':[1,0.1, 0.01, 0.001],'degree':[2,3,5]}]
clf=GridSearchCV(estimator=svm.SVC(),param_grid=parameters,cv=5,scoring='accuracy')
clf.fit(X_train,y_train)
print("最好的超参数:",clf.best_params_)
print("最好的分数为:",clf.best_score_)
#使用最好的模型
best_svc=clf.best_estimator_
train_pred=best_svc.predict(X_train)
test_pred=best_svc.predict(X_test)
print('----------模型在训练集的结果-------')
train_acc=metrics.accuracy_score(y_train,train_pred)
train_cm=metrics.confusion_matrix(y_train,train_pred)
train_report=metrics.classification_report(y_train,train_pred)
print("训练集准确率为:",train_acc)
print("-----------------")
print("训练集混淆矩阵:",train_cm)
print("-----------------")
print("训练集分类报告:",train_report)
print('----------模型在测试集的结果-------')
test_acc=metrics.accuracy_score(y_test,test_pred)
test_cm=metrics.confusion_matrix(y_test,test_pred)
test_report=metrics.classification_report(y_test,test_pred)
print("测试集准确率为:",test_acc)
print("-----------------")
print("测试集混淆矩阵:",test_cm)
print("-----------------")
print("测试集分类报告:",test_report)
plot_confusion_matrix(test_cm)
优化后模型在测试集上的准确率有95%,较没有超参数的模型有了较大的提升,所以还是比较好的啦。并且可以看到混淆矩阵只有第1,5两个类别错误,具体原因最后会做分析。
再来看看神经网络的效果
import tensorflow as tf
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers import Conv2D, Dense, AveragePooling2D, Flatten, BatchNormalization
from keras.optimizers import Adam
#reshape
X_train = X_train.astype('float32').values.reshape(len(X_train),3,3,1)
X_test = X_test.astype('float32').values.reshape(len(X_test),3,3,1)
#因为类别不是从0开始编号,所以进行one-hot编码时减1
Y_train = np_utils.to_categorical(y_train-1)
Y_test = np_utils.to_categorical(y_test-1)
搭建网络结构,其中Adam的参数是根据实验的最好结果,我也就没去改
model = Sequential()
model.add(Conv2D(12,6, strides=1, padding='same', input_shape=(3,3,1), activation='relu'))
model.add(AveragePooling2D(3,2, padding='same'))
model.add(BatchNormalization())
model.add(Conv2D(9,9, strides=1, padding='same', activation='relu'))
model.add(BatchNormalization())
model.add(AveragePooling2D(12,3, padding='same'))
model.add(Flatten())
model.add(Dense(16, activation='relu'))
model.add(Dense(8, activation='relu'))
model.add(Dense(5, activation='softmax'))
adam = Adam(lr=0.001, beta_1=0.9, beta_2=0.999,epsilon=1e-8)
model.compile(optimizer=adam, loss='categorical_crossentropy', metrics=['accuracy'])
print('Training')
result = model.fit(X_train, Y_train, epochs=40, batch_size=6,validation_split=0.1,shuffle=True)
print('Testing')
loss, accuracy = model.evaluate(X_test, Y_test)
print('loss, accuracy', loss, accuracy)
N=40
plt.plot(np.arange(0,N),result.history["loss"],label ="train_loss")
plt.plot(np.arange(0,N),result.history["val_loss"],label="val_loss")
plt.plot(np.arange(0,N),result.history["accuracy"],label="train_acc")
plt.plot(np.arange(0,N),result.history["val_accuracy"],label="val_acc")
plt.title("loss and accuracy")
plt.xlabel("epoch")
plt.ylabel("loss/acc")
plt.legend(loc="best")
plt.show()
cnn_pred=model.predict(X_test)
cnn_pred=np.argmax(cnn_pred,axis=1)+1
cnn_testcm=metrics.confusion_matrix(y_test,cnn_pred)
plot_confusion_matrix(cnn_testcm)
发现神经网络不太理想哈,测试集上准确率只有88%,从训练过程来看训练曲线都很陡峭,而不是所期待的平滑的下降,也存在欠拟合的问题,还是需要改进的。
思考与改进
- 1.为什么模型的效果并不高呢?
通过查看图片会发现其实类别五的图片样本很少,只有四五张,所以从混淆矩阵看得出来类别五一直都是处于错分的情况。 - 2.改进的一些想法
数据集中1,2,3类别的图片很多,4,5很少,所以我们划分训练集和测试集的时候是不是可以追求更平均一点而不是随机打乱。而且对于神经网络来说,我们得到的颜色矩给他计算的样本数量其实是不够的,我们完全可以整张图片作为输入又或者是对图片做一些数据增强,事实上增加输入和进行数据增强神经网络模型的效果是有很大提升的,然后对于神经网络的参数问题和激活函数的选择这个还是靠经验+尝试吧
可以看到处理后的数据训练曲线平滑了很多而且准确率也基本是稳定上升,这一部分的代码我就不贴了,只不过就是把之前的步骤重新来一遍不做颜色矩处理直接划分数据集然后输入而已,另外我们如果发现有过拟合的情况可以试试引入早停。
结束
差不多就是这些了,也不知道大家考研复习的怎么样不过我真的好想去学校呀。对于懒懒的我,床在旁边一躺一天,哈哈哈。反正不管怎么样,加油吧。