從年初起,幾家國際大廠的開發(fā)者大會,無論是微軟Build、Facebook F8還是稍后的Google I/O,莫不把“AI優(yōu)先”的大旗扯上云霄。
如果這一波AI大潮只是空喊幾句口號,空提幾個戰(zhàn)略,空有幾家炙手可熱的創(chuàng)業(yè)公司,那當(dāng)然成不了什么大氣候。但風(fēng)浪之下,我們看到的卻是,Google一線的各大業(yè)務(wù)紛紛改用深度學(xué)習(xí),落伍移動時代的微軟則已拉起一支近萬人的AI隊伍。而國內(nèi)一線大廠的情況,更是把AI牢牢把握住,試圖再創(chuàng)高峰。
今天本文將分享一篇AI入門實戰(zhàn)的項目經(jīng)驗分享,手把手帶你進(jìn)入AI的世界,讓你消除對AI技術(shù)壁壘過高的恐懼~
【AI項目實戰(zhàn)】多標(biāo)簽圖像分類競賽小試牛刀
初次拿到這個題目,想了想做過了貓狗大戰(zhàn)這樣的二分類,也做過cifar-10這樣的多分類,類似本次比賽的題目多標(biāo)簽圖像分類的確沒有嘗試過。6941個標(biāo)簽,每張圖片可能沒有標(biāo)簽也可能存在6941個標(biāo)簽,即各個標(biāo)簽之間是不存在互斥關(guān)系的,所以最終分類的損失函數(shù)不能用softmax而必須要用sigmoid。然后把分類層預(yù)測6941個神經(jīng)元,每個神經(jīng)元用sigmoid函數(shù)返回是否存在某個標(biāo)簽即可。
來蹚下整個流程看看,在jupyter notebook上做得比較亂,但是整個流程還是可以看出來的。深度學(xué)習(xí)模型用的Keras。
先導(dǎo)入train_csv數(shù)據(jù),這里用的是最初版的訓(xùn)練csv文件,img_path里存在地址,后面做了處理。
code
importpandasaspdimportnumpyasnpimportmatplotlib.pyplotasplt%matplotlibinlinefromglobimportglobfromtqdmimporttqdmimportcv2fromPILimportImagetrain_path='visual_china_train.csv'train_df=pd.read_csv(train_path)train_df.head()
codetrain_df.shape#(35000,2)
可以看到總共有35000張訓(xùn)練圖片,第一列為圖片名稱(帶地址,需處理),第二列為圖片對應(yīng)標(biāo)簽。
來看下是不是的確只有6941個標(biāo)簽:
code
tags=[]foriinrange(train_df['tags'].shape[0]):fortagintrain_df['tags'].iloc[i].split(','):tags.append(tag)tags=set(tags)len(tags)#6941
事實證明標(biāo)簽總數(shù)無誤,可以放心大膽地繼續(xù)進(jìn)行下去了。
然后我處理了下圖片名稱,并存到了img_paths列表里。
code
#如果使用的是官方后來更新的visual_china_train.csv,可以直接使用最后一行代碼foriinrange(35000):train_df['img_path'].iloc[i]=train_df['img_path'].iloc[i].split('/')[-1]img_paths=list(train_df['img_path'])
定義三個函數(shù),其中:
hash_tag函數(shù)讀入valid_tags.txt文件,并存入字典,形成索引和標(biāo)簽的對照。
load_ytrain函數(shù)讀入tag_train.npz文件,并返回訓(xùn)練集的y_train,形式為ndarray,shape為(35000, 6941),即35000張圖片和對應(yīng)標(biāo)簽的one-hot編碼。
arr2tag函數(shù)將預(yù)測結(jié)果的y_pred轉(zhuǎn)變成對應(yīng)的中文標(biāo)簽。(實際上最后還需要做下處理)
code
defhash_tag(filepath):fo=open(filepath,"r",encoding='utf-8')hash_tag={}i=0forlineinfo.readlines():#依次讀取每行l(wèi)ine=line.strip()#去掉每行頭尾空白hash_tag[i]=linei+=1returnhash_tagdefload_ytrain(filepath):y_train=np.load(filepath)y_train=y_train['tag_train']returny_traindefarr2tag(arr):tags=[]foriinrange(arr.shape[0]):tag=[]index=np.where(arr[i]>0.5)index=index[0].tolist()tag=[hash_tag[j]forjinindex]tags.append(tag)returntags
讀入valid_tags.txt,并生成索引和標(biāo)簽的映射。
code
filepath="valid_tags.txt"hash_tag=hash_tag(filepath)hash_tag[1]#'0到1個月'
載入y_train
code
y_train=load_ytrain('tag_train.npz')y_train.shape#(35000,6941)
前期準(zhǔn)備工作差不多做完了,開始導(dǎo)入訓(xùn)練集。此處有個坑,即原始訓(xùn)練集中存在CMYK格式的圖片,傳統(tǒng)圖片處理一般為RGB格式,所以使用Image庫中的convert函數(shù)對非RGB格式的圖片進(jìn)行轉(zhuǎn)換。
code
nub_train=5000#可修改,前期嘗試少量數(shù)據(jù)驗證模型X_train=np.zeros((nub_train,224,224,3),dtype=np.uint8)i=0forimg_pathinimg_paths[:nub_train]:img=Image.open('train/'+img_path)ifimg.mode!='RGB':img=img.convert('RGB')img=img.resize((224,224))arr=np.asarray(img)X_train[i,:,:,:]=arri+=1
訓(xùn)練集導(dǎo)入完成,來看圖片的樣子,判斷下圖片有沒有讀入錯誤之類的問題。
code
fig,axes=plt.subplots(6,6,figsize=(20,20))j=0fori,imginenumerate(X_train[:36]):axes[i//6,j%6].imshow(img)j+=1
看樣子還不錯,go on! 訓(xùn)練集的X_train、y_train都拿到了,分割出驗證集。這里要說明一下,官方的y_train里圖片名稱與X_train里圖片名稱是對應(yīng)的所以可以直接分割。
code
fromsklearn.model_selectionimporttrain_test_splitX_train2,X_val,y_train2,y_val=train_test_split(X_train,y_train[:nub_train],test_size=0.2,random_state=2018)
數(shù)據(jù)準(zhǔn)備完成,開始搭建模型。咳咳,先從簡單的入手哈,此模型仿tinymind上一次的漢字書法識別大賽中“真的學(xué)不會”大佬的結(jié)構(gòu)來搭的,又加了些自己的東西,反正簡單模型試試水嘛。
code
fromkeras.layersimport*fromkeras.modelsimport*fromkeras.optimizersimport*fromkeras.callbacksimport*defbn_prelu(x):x=BatchNormalization()(x)x=PReLU()(x)returnxdefbuild_model(out_dims,input_shape=(224,224,3)):inputs_dim=Input(input_shape)x=Lambda(lambdax:x/255.0)(inputs_dim)#在模型里進(jìn)行歸一化預(yù)處理x=Conv2D(16,(3,3),strides=(2,2),padding='same')(x)x=bn_prelu(x)x=Conv2D(16,(3,3),strides=(1,1),padding='same')(x)x=bn_prelu(x)x=MaxPool2D(pool_size=(2,2))(x)x=Conv2D(32,(3,3),strides=(1,1),padding='same')(x)x=bn_prelu(x)x=Conv2D(32,(3,3),strides=(1,1),padding='same')(x)x=bn_prelu(x)x=MaxPool2D(pool_size=(2,2))(x)x=Conv2D(64,(3,3),strides=(1,1),padding='same')(x)x=bn_prelu(x)x=MaxPool2D(pool_size=(2,2))(x)x=Conv2D(128,(3,3),strides=(1,1),padding='same')(x)x=bn_prelu(x)x=GlobalAveragePooling2D()(x)dp_1=Dropout(0.5)(x)fc2=Dense(out_dims)(dp_1)fc2=Activation('sigmoid')(fc2)#此處注意,為sigmoid函數(shù)model=Model(inputs=inputs_dim,outputs=fc2)returnmodel
看下模型結(jié)構(gòu):
code
model=build_model(6941)model.summary()_________________________________________________________________Layer(type)OutputShapeParam# =================================================================input_1(InputLayer)(None,224,224,3)0 _________________________________________________________________lambda_1(Lambda)(None,224,224,3)0 _________________________________________________________________conv2d_1(Conv2D)(None,112,112,16)448 _________________________________________________________________batch_normalization_1(Batch(None,112,112,16)64 _________________________________________________________________p_re_lu_1(PReLU)(None,112,112,16)200704 _________________________________________________________________conv2d_2(Conv2D)(None,112,112,16)2320 _________________________________________________________________batch_normalization_2(Batch(None,112,112,16)64 _________________________________________________________________p_re_lu_2(PReLU)(None,112,112,16)200704 _________________________________________________________________max_pooling2d_1(MaxPooling2(None,56,56,16)0 _________________________________________________________________conv2d_3(Conv2D)(None,56,56,32)4640 _________________________________________________________________batch_normalization_3(Batch(None,56,56,32)128 _________________________________________________________________p_re_lu_3(PReLU)(None,56,56,32)100352 _________________________________________________________________conv2d_4(Conv2D)(None,56,56,32)9248 _________________________________________________________________batch_normalization_4(Batch(None,56,56,32)128 _________________________________________________________________p_re_lu_4(PReLU)(None,56,56,32)100352 _________________________________________________________________max_pooling2d_2(MaxPooling2(None,28,28,32)0 _________________________________________________________________conv2d_5(Conv2D)(None,28,28,64)18496 _________________________________________________________________batch_normalization_5(Batch(None,28,28,64)256 _________________________________________________________________p_re_lu_5(PReLU)(None,28,28,64)50176 _________________________________________________________________max_pooling2d_3(MaxPooling2(None,14,14,64)0 _________________________________________________________________conv2d_6(Conv2D)(None,14,14,128)73856 _________________________________________________________________batch_normalization_6(Batch(None,14,14,128)512 _________________________________________________________________p_re_lu_6(PReLU)(None,14,14,128)25088 _________________________________________________________________global_average_pooling2d_1((None,128)0 _________________________________________________________________dropout_1(Dropout)(None,128)0 _________________________________________________________________dense_1(Dense)(None,6941)895389 _________________________________________________________________activation_1(Activation)(None,6941)0 =================================================================Totalparams:1,682,925 Trainableparams:1,682,349 Non-trainableparams:576_________________________________________________________________
由于比賽要求里最終得分標(biāo)準(zhǔn)是fmeasure而不是acc,故網(wǎng)上找來一段代碼用以監(jiān)測訓(xùn)練中查準(zhǔn)率、查全率、fmeasure的變化。原地址找不到了,故而無法貼上,罪過罪過。
code
importkeras.backendasKdefprecision(y_true,y_pred):#Calculatestheprecisiontrue_positives=K.sum(K.round(K.clip(y_true*y_pred,0,1)))predicted_positives=K.sum(K.round(K.clip(y_pred,0,1)))precision=true_positives/(predicted_positives+K.epsilon())returnprecisiondefrecall(y_true,y_pred):#Calculatestherecalltrue_positives=K.sum(K.round(K.clip(y_true*y_pred,0,1)))possible_positives=K.sum(K.round(K.clip(y_true,0,1)))recall=true_positives/(possible_positives+K.epsilon())returnrecalldeffbeta_score(y_true,y_pred,beta=1):#CalculatestheFscore,theweightedharmonicmeanofprecisionandrecall.ifbeta0:????????raise?ValueError('The?lowest?choosable?beta?is?zero?(only?precision).')????#?If?there?are?no?true?positives,?fix?the?F?score?at?0?like?sklearn.????if?K.sum(K.round(K.clip(y_true,?0,?1)))?==?0:????????return?0????p?=?precision(y_true,?y_pred)????r?=?recall(y_true,?y_pred)????bb?=?beta?**?2????fbeta_score?=?(1?+?bb)?*?(p?*?r)?/?(bb?*?p?+?r?+?K.epsilon())????return?fbeta_scoredef?fmeasure(y_true,?y_pred):????#?Calculates?the?f-measure,?the?harmonic?mean?of?precision?and?recall.????return?fbeta_score(y_true,?y_pred,?beta=1)
這里稍做圖片增強,用Keras里的ImageDataGenerator函數(shù),同時還可生成器方法進(jìn)行訓(xùn)練。
code
fromkeras.preprocessing.imageimportImageDataGeneratortrain_datagen=ImageDataGenerator(width_shift_range=0.1,height_shift_range=0.1,zoom_range=0.1)val_datagen=ImageDataGenerator()#驗證集不做圖片增強batch_size=8train_generator=train_datagen.flow(X_train2,y_train2,batch_size=batch_size,shuffle=False)val_generator=val_datagen.flow(X_val,y_val,batch_size=batch_size,shuffle=False)
開始訓(xùn)練。這里在ModelCheckpoint里設(shè)置monitor監(jiān)控feasure,mode為max,不再以最低loss作為模型最優(yōu)的判斷標(biāo)準(zhǔn)(個人做法,好壞可自行實驗判斷)。
code
checkpointer=ModelCheckpoint(filepath='weights_best_simple_model.hdf5',monitor='val_fmeasure',verbose=1,save_best_only=True,mode='max')reduce=ReduceLROnPlateau(monitor='val_fmeasure',factor=0.5,patience=2,verbose=1,min_delta=1e-4,mode='max')model.compile(optimizer='adam',loss='binary_crossentropy',metrics=['accuracy',fmeasure,recall,precision])epochs=20history=model.fit_generator(train_generator,validation_data=val_generator,epochs=epochs,callbacks=[checkpointer,reduce],verbose=1)
訓(xùn)練了20個epoch,這里給出第20個epoch時的訓(xùn)練結(jié)果,可以看到,val_loss 0.0233,其實已經(jīng)挺低了;val_acc0.9945,參考意義不大(暫時不清楚有什么參考意義~~);val_fmeasure0.17,嗯。。任重道遠(yuǎn)啊。
訓(xùn)練了20個epoch,這里給出第20個epoch時的訓(xùn)練結(jié)果,可以看到,val_loss 0.0233,其實已經(jīng)挺低了;val_acc0.9945,參考意義不大(暫時不清楚有什么參考意義~~);val_fmeasure0.17,嗯。。任重道遠(yuǎn)啊。
Epoch20/20500/500[==============================]-48s96ms/step-loss:0.0233-acc:0.9946-fmeasure:0.1699-recall:0.0970-precision:0.7108-val_loss:0.0233-val_acc:0.9946-val_fmeasure:0.1700-val_recall:0.0968-val_precision:0.7162 Epoch00020:val_fmeasuredidnotimprovefrom0.17148
以上只給出5000張圖片的簡單模型訓(xùn)練方法,但數(shù)據(jù)處理,搭建模型以及訓(xùn)練過程已經(jīng)很清晰明了了,后面的進(jìn)階之路就憑大家各顯身手了。
然后開始進(jìn)行預(yù)測,導(dǎo)入測試集(當(dāng)然是在訓(xùn)練集全部訓(xùn)練之后再進(jìn)行測試集的預(yù)測)。
code
nub_test=len(glob('valid/*'))X_test=np.zeros((nub_test,224,224,3),dtype=np.uint8)path=[]i=0forimg_pathintqdm(glob('valid/*')):img=Image.open(img_path)ifimg.mode!='RGB':img=img.convert('RGB')img=img.resize((224,224))arr=np.asarray(img)X_test[i,:,:,:]=arri+=1100%|██████████████████████████████████████████████████████████████████████████████|8000/8000[02:18<00:00,?57.91it/s]
預(yù)測測試集并利用arr2tag函數(shù)將結(jié)果轉(zhuǎn)為中文標(biāo)簽,以便生成提交文件。
code
y_pred=model.predict(X_test)y_tags=arr2tag(y_pred)
生成提交文件:
code
importosimg_name=os.listdir('valid/')img_name[:10]['000effcf2091ae3895074838b7e5f571186ab362.jpg', '0014455e5fbfd0961039fe23675debbb1a7b2308.jpg', '002138959ee7a14eb2860100392a384f8a85425f.jpg', '002414411ce17c6c7ab5d36dd3f956d0691ba495.jpg', '002780359fda7f09e6d1fc52d88aff90c6e8298b.jpg', '002ad24891ddf815bb86e4eca34415b1b44c9e4b.jpg', '002c284f94299bcee51733f7d6b17f3e4792d8c5.jpg', '002cf4b15887f32b688113a2a7a3f5786896d019.jpg', '003d4c12160b90fbbb2bd034ee30c251a45d9037.jpg', '0043ab4460cc79bfbea3db69d2a55d5f35600a37.jpg']
arr2tag函數(shù)得到的每張圖片的標(biāo)簽是list格式,需轉(zhuǎn)成str,在這里操作。經(jīng)實驗,windows中的方法與ubuntu中不同,后面也給出了ubuntu中本步的處理方法。
code
#windowsimportpandasaspddf=pd.DataFrame({'img_path':img_name,'tags':y_tags})foriinrange(df['tags'].shape[0]):df['tags'].iloc[i]=','.join(str(e)foreindf['tags'].iloc[i])df.to_csv('submit.csv',index=None)df.head()
code
##Ubuntuimportpandasaspddf=pd.DataFrame({'img_path':img_name,'tags':y_tags})foriinrange(df['tags'].shape[0]):df['tags'].iloc[i]=df['tags'].iloc[i][2:-2].replace(''',"").replace(''',"")df.to_csv('submit.csv',index=None)
整篇到此結(jié)束,有幾點要說的:
提高方法。不用說,肯定是上預(yù)訓(xùn)練模型,可能再進(jìn)行模型融合效果會更好。官方大大說整個標(biāo)簽由于人工標(biāo)注,可能會跟機器預(yù)測出來的有別差,畢竟看預(yù)測結(jié)果中出現(xiàn)的 “一個人,人,僅一個女人,僅一個青年女人,僅女人,僅成年人” ,如果由人類來標(biāo)注可能不會這么啰嗦~~所以可以考慮NLP方法對標(biāo)簽進(jìn)行一些處理(我不會)。另外網(wǎng)上查到了個詭異的做法,說可以把fmeasure變成損失函數(shù)去訓(xùn)練模型(fmeasure不可導(dǎo)),我想如果有辦法做到應(yīng)該效果不錯吧。
不足之處。訓(xùn)練過程中監(jiān)控fmeasure和監(jiān)控loss的做法,看上去應(yīng)該是fmeasure沒錯,不過自己對于這塊研究不夠,只能憑感覺在做,各位看官可自由發(fā)揮。
整篇文章代碼只有查準(zhǔn)率、查全率、fmeasure部分為網(wǎng)上摘取,其他均為原創(chuàng)代碼(略有借鑒),其實是想說,代碼可能有些地方稚嫩,還望各位大佬們海涵。
如果想深入學(xué)習(xí)的話,我推薦還是報名實訓(xùn)營,讓更有經(jīng)驗的大咖導(dǎo)師為你指路,效率和效果都會翻倍!
在這里推薦 CSDN 學(xué)院出品的《人工智能工程師直通車》實訓(xùn)營,目的是:通過 120 天的實戰(zhàn),將學(xué)員培養(yǎng)達(dá)到具備一年項目經(jīng)驗的人工智能工程師水平。CSDN 百天計劃課程共分為 3 個階段,4 個月完成。
-
神經(jīng)元
+關(guān)注
關(guān)注
1文章
363瀏覽量
18499 -
ai技術(shù)
+關(guān)注
關(guān)注
1文章
1289瀏覽量
24427
原文標(biāo)題:沒練過這個項目,怎么做AI工程師?
文章出處:【微信號:rgznai100,微信公眾號:rgznai100】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論