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很少,所以我們劃分訓練集和測試集的時候是不是可以追求更平均一點而不是隨機打亂。而且對於神經網絡來說,我們得到的顏色矩給他計算的樣本數量其實是不夠的,我們完全可以整張圖片作爲輸入又或者是對圖片做一些數據增強,事實上增加輸入和進行數據增強神經網絡模型的效果是有很大提升的,然後對於神經網絡的參數問題和激活函數的選擇這個還是靠經驗+嘗試吧
可以看到處理後的數據訓練曲線平滑了很多而且準確率也基本是穩定上升,這一部分的代碼我就不貼了,只不過就是把之前的步驟重新來一遍不做顏色矩處理直接劃分數據集然後輸入而已,另外我們如果發現有過擬合的情況可以試試引入早停。
結束
差不多就是這些了,也不知道大家考研複習的怎麼樣不過我真的好想去學校呀。對於懶懶的我,牀在旁邊一躺一天,哈哈哈。反正不管怎麼樣,加油吧。