-
獲得訓練圖片的路徑和標簽
load_files()
函數(shù)讀取之后輸出的是文本文檔
filenames 的輸出是文件的路徑
target 的輸出是子文件夾的名字。在這里子文件夾名字是 c0,c1,c2,c3...c9穷当。但是讀取出來的是0,1,2,3,....9辰企。不過不會影響使用宠能,后面要轉(zhuǎn)化成為獨熱編碼方灾。
*特別注意:如果讀取的文件夾下面沒有子文件夾塘秦,命令用不了讼渊,也就是沒有'target'的值。
def img_load(path):
data = load_files(path)
files = data['filenames']
#targets = np_utils.to_categorical(np.array(data['target']), 10)
targets = data['target']
return files, targets
-
獲得文件名&路徑
for maindir, subdir, file_name_list in os.walk(dirname):
命令讀取文件目錄尊剔,文件上一級目錄爪幻,文件名。os.walk()輸出的是三個值(列表)。
如果要組成完整的文件目錄挨稿,需要再加工一步:
apath = os.path.join(maindir, filename)
def path_and_name(dirname):
result = []
for maindir, subdir, file_name_list in os.walk(dirname):
for filename in file_name_list:
apath = os.path.join(maindir, filename)
result.append(apath)
name = file_name_list
return result,name
-
讀取圖片為array
cv2.imread().astpye(np.uint8)
讀取圖片的時候仇轻,默認格式是np.float64
,在這里為了省內(nèi)存奶甘,用np.uint8
矩陣2個常用的命令
np.expand_dims(x,axis=0)
此為增加維度命令篷店。axis值代表的是在什么地方增加維度。0
代表在最前面增加
x=x.transpose((2,0,1))
此為轉(zhuǎn)換維度順序臭家。原本是(0疲陕,1,2)的3維矩陣钉赁,轉(zhuǎn)換成了(2蹄殃,0,1)你踩。最常見的情況就是將顏色通道放在最前面/最后面
要查看某個矩形的形狀 x.shape
def img_read(path,img_cols=299, img_rows=299):
img = cv2.imread(path).astype(np.uint8)
img_newsize = cv2.resize(img,(img_cols,img_rows))
#img_newsize.transpose((2,0,1))
#np.expand_dims(img_newsize, axis=2)
return img_newsize
-
按司機ID來劃分訓練集
由于一個司機ID的圖像是從視頻里面截取出來的诅岩,所以如果訓練的時候,訓練集合驗證集都有同一個ID的圖像带膜》郧基本可以說是判斷準確率100%了。這樣訓練的分數(shù)高膝藕,實際在其他地方很低式廷。
劃分ID是讀取了CSV的文件。使用的命令包括
pd.read_csv()
讀取CSV文件之后束莫,list.index
代表了第一列
隨后使用set()簡單粗暴的去除重復懒棉。
list.classname
可以獲得分類的名字,其中'classname'是表格中的表頭內(nèi)容
list.img
可以獲得圖片的名字
- 用遷移學習提取特征
這是一次失敗的嘗試览绿。最開始使用可VGG19來提取特征,然后分類穗慕,效果很不理想饿敲。推斷是VGG19預訓練的模型(在ImageNet預訓練)和司機的圖片契合度太差了。
def found_features(path):
base_model = VGG19(weights = 'imagenet')
x = img_read(path)
x = np.expand_dims(x, axis=0)
base_model = bese_model
model = Model(inputs=base_model.input,
outputs=base_model.get_layer('block4_pool').output)
return model.predict(x)
-
保存model文件
先創(chuàng)建一個文件夾來保存h5文件逛绵,判斷如果文件夾不存在怀各,則創(chuàng)立新的文件夾'cache'。使用命令:
if not os.path.isdir('cache'): os.mkdir('cache')
保存文件术浪,在這里使用overwrite=True
def save_model(model, index, cross=''):
json_string = model.to_json()
if not os.path.isdir('cache'):
os.mkdir('cache')
json_name = 'architecture' + str(index) + cross + '.json'
weight_name = 'model_weights' + str(index) + cross + '.h5'
#open(os.path.join('cache', json_name), 'w').write(json_string)
model.save_weights(os.path.join('cache', weight_name), overwrite=True)
-按司機ID來創(chuàng)立列表
一級列表有26個子列表(司機ID26個)瓢对,每個二級列表里包含的是一個司機ID對應的圖片路徑。
同樣的標簽也按同樣的方法來創(chuàng)立成兩級列表胰苏,與圖片路徑一一對應硕蛹。
來獲取一個路徑末尾文件名的方法如下:
os.path.basename(X_path[ii])
獲得pandas.DataFrame 文件某個index下的值的方法:
img_list.loc['c1'].values
為了方便查看進度,只打印一排內(nèi)容,在后面加上 ,
表示不換行法焰。
def X_and_y(X_path,y_,img_list,ID):
print('按司機ID分類數(shù)據(jù)')
X = []
y = []
nu = 0
for i in ID:
X_array = []
y_array = []
for ii in range(len(X_path)):
if os.path.basename(X_path[ii]) in img_list.loc[i].values:
#x_ar = found_features(X_path[ii]) #將在ID下圖片的提取特征
x_ar = X_path[ii]
X_array.append(x_ar) #形式為 [[特征 ],[特征 ] ...]
y_array.append(y_[ii])
print("{}/{} Data named {}.........{}files\r".
format(nu+1,len(ID),i,len(img_list.loc[i]))),
X.append(X_array) #形式為 [ [[ID1_特征 ],[ID1_特征 ] ...] ,[[ID2_特征 ] [ID2_特征 ] ...] ,...]
y.append(y_array)
nu += 1
return X,y
-
執(zhí)行函數(shù)秧荆,分別獲得訓練圖片路徑以及標簽的雙層列表
ID,classname,list_img_name,img_list = driver_id('driver_imgs_list.csv',index_col = 'subject')
X_path,y_ = img_load('train')
X_data,y_data = X_and_y(X_path,y_,img_list,ID)
-
自己先做一個測試集出來檢查算法
從訓練集中拿出兩個ID下的子列表出來,做成自己的訓練集埃仪。這個時候就要把原來的訓練集和測試集##減去##兩個ID乙濒,使用的是:del X_data[:2]
。 這個命令只能執(zhí)行一次卵蛉,執(zhí)行1次就會扣除兩個子列表颁股。
既然是做測試集,這里就不需要再按ID分類了傻丝,在這一步就把兩層列表變?yōu)橐粚樱?/p>
test_fe,test_la = X_data[:2],y_data[:2]
del X_data[:2]
del y_data[:2]
#制作自己的test先自己測試一次
test_feature = []
test_label = []
for a in test_fe:
for i in a:
test_feature.append(i)
for b in test_la:
for ii in b:
test_label.append(ii)
print(type(test_feature),len(test_label))
print(len(X_data))
print(len(y_data))
-
模型搭建的種種過程
-
使用遷移學習直接訓練模型
首先使用了VGG19的預訓練模型甘有。
loss函數(shù)~2.0,而準確率還可以接近1运准。一定是哪里出了問題幌氮。
在第一時間想到的是模型上的問題(其實不是)。
-
使用遷移學習提取特征
采用了VGG19/Xception 來提取特征胁澳。在這里遇到的麻煩是:
(1) VGG輸入圖片格式為(244该互,244,3),而Xception輸入圖片格式為(299韭畸,299宇智,3)。因此在前面定義的def img_read()
函數(shù)中胰丁,需要更改一下圖片的reshape随橘。
(2)提取出來的特征展平做全連接層的時候出現(xiàn)了內(nèi)存錯誤。
-
從頭搭建網(wǎng)絡
從頭搭建VGG16網(wǎng)絡锦庸,采用的是序貫模型机蔗。搭建好網(wǎng)絡之后載入下載的VGG16權(quán)重。
然后使用model.layers.pop()
去除最后一個輸出層(1000個分類)繼續(xù)添加全連接層和softmax分類層甘萧。
訓練幾次之后發(fā)現(xiàn)效果仍然不佳萝嘁。采用了如下方法:
(1)添加dropout,估計是過擬合扬卷。添加之后訓練過程的分數(shù)與之前無大的進步牙言。
(2)仔細檢查特征和標簽。發(fā)現(xiàn)是在寫入時有誤(下一節(jié)仔細分析)
(3)對模型使用正則怪得。
model.add(Dense(64, input_dim=64,kernel_regularizer=regularizers.l2(0.001), activity_regularizer=regularizers.l1(0.001)))
(4)調(diào)整學習率 lr=le-1/ -2/ -3/ -4 /-5
(5)不載入權(quán)重咱枉,從頭訓練
-
從頭搭建Xception網(wǎng)絡
Xception搭建需要使用函數(shù)式模型卑硫。函數(shù)式模型可以有多個輸入以及多個輸出。
載入Xception 沒有top的權(quán)重庞钢,然后繼續(xù)分類拔恰。在這里我犯了一個觀念上的錯誤,在添加后面的層的時候還使用序貫模型的做法基括,報錯颜懊。
為了使模型可視化,有兩中辦法:
(1)采用 model.summary()
輸出簡單的模型內(nèi)容风皿『拥可以看到Output Shape,Param, Connected to 這三個內(nèi)容桐款。
(2)將模型打印成PNG圖片保存:
from keras.utils import plot_model
plot_model(model, to_file='model.png')
可以接收兩個參數(shù):
show_shapes
:指定是否顯示輸出數(shù)據(jù)的形狀咸这,默認為False
show_layer_names
:指定是否顯示層名稱,默認為True
-
采用遷移學習,融合多個模型
融合了Xception以及InceptionV3模型魔眨,將兩個模型的倒數(shù)第二層作為輸出媳维。沒有在各自遷移學習模型里用池化層。
使用函數(shù)模型將兩個結(jié)合在一起遏暴,結(jié)合之前由于輸入的形狀不一致侄刽,((None,10,10,2048)(None,8,8,2048)),因此采用GAP將輸出轉(zhuǎn)為一維朋凉,然后再進行聯(lián)合州丹。
在這里有幾個注意的地方:
(1)在這里使用的是tensor
(2)在輸入數(shù)據(jù)前需要使用Input()
函數(shù)
第一次完全在訓練之后發(fā)現(xiàn)分數(shù)只達到了0.4,第二次采用方法為InceptionV3不載入預訓練權(quán)重杂彭。
兩次訓練的分數(shù)為0.4和0.6
-
準備訓練數(shù)據(jù)
在訓練的時候才讀入圖片為(299,299,3)的格式墓毒。在這里有幾個注意點:
(1)根據(jù)訓練集的大小先創(chuàng)建好空的矩陣,再逐個寫入亲怠。train_feature_A = np.zeros(shape=[number_of_train_A,input_l,input_w, input_c])
(2)標簽要轉(zhuǎn)換成為獨熱編碼:np_utils.to_categorical(np.array(y_data_A[i][N]),10))
(3)在訓練時候前幾次使用了keras自帶的數(shù)據(jù)預處理命令preprocess_input(train_feature_A)
所计。經(jīng)過測驗,這樣的預處理出來預測值非常極端团秽。后來直接采用的是不預處理的 np.uint8 格式數(shù)據(jù)醉箕。
(4)訓練過程中最常常遇到的問題是內(nèi)存不足的問題。最終的解決辦法是每一折訓練都保存權(quán)重文件徙垫。如果內(nèi)存不足,重新載入權(quán)重繼續(xù)訓練放棒。在這里的訓練時長就比較長了:
image.png 使用模型融合
使用整合訓練姻报,普通堆疊。堆疊方法由上一篇翻譯文得出:kaggle 模型融合指導
-
預測測試集间螟,制作提交文件
預測的時候的幾個細節(jié):
- 仍然是內(nèi)存問題吴旋。由于預測文件高達有79726個损肛,在準備測試集的時候。采用創(chuàng)建空矩陣再逐個寫入的辦法荣瑟,發(fā)現(xiàn)內(nèi)存又不夠了治拿。所以每次預測都先退出清空內(nèi)存之后,只運行載入模型和訓練好權(quán)重再預測笆焰,沒有辦法接上一歩訓練模型后繼續(xù)預測劫谅。
- 由于測試集沒有子目錄,所以不能使用第一步的方法取得文件路徑嚷掠。這里使用的命令是:
for maindir, subdir, file_name in os.walk('test'):
必須要取得文件名的原因是在提交文件CSV里面index為img名稱捏检。 - 在準備提交文件的時候,由于后面采用了兩個模型整合預測不皆,占用內(nèi)存增大贯城,無論如何都無法創(chuàng)建測試集數(shù)目大小的空矩陣。
于是采用了逐行寫入CSV的辦法霹娄,每一個圖片單獨預測能犯,得到10個分類的概率之后寫入文件:
def write_csv(path):
path = all_path(path)
header = ['img', 'c0', 'c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7','c8', 'c9']
number_all = len(path)
with open('subm/kjkj.csv', 'wb')as f:
f_csv = csv.DictWriter(f,header)
f_csv.writeheader()
path = path
n =1
for i in path:
img_name = os.path.basename(i)
img_arr = img_read(i)
img_arr = np.expand_dims(img_arr, axis=0)
p = model.predict(img_arr)
row = [{'img':img_name,
'c0':p[0][0],
'c1':p[0][1],
'c2':p[0][2],
'c3':p[0][3],
'c4':p[0][4],
'c5':p[0][5],
'c6':p[0][6],
'c7':p[0][7],
'c8':p[0][8],
'c9':p[0][9] }]
f_csv.writerows(row)
print('pre {} complete!.........{}/{}\r'.format(n,n,number_all)),
n+=1
-
使用CMA查看模型關注的地方