貓狗大戰挑戰賽
VGG實現貓狗分類
1、匯入資料和解壓資料
! wget http://fenggao-image.stor.sinaapp.com/dogscats.zip! unzip dogscats.zip ! wget https://static.leiphone.com/cat_dog.rar! unrar x cat_dog.rar
分別匯入AI研習社的資料和老師給的資料,本來想本地匯入,由於太卡,上傳了一個小時沒上傳到google網盤,網盤還一直掉線,因此放棄,下載了兩組資料。dogscats資料中的train和val分別有cat和dog的資料夾,因此選用dogscats的資料做實驗。
2、設定transform函式,將AI研習社的test資料夾提取到同一個資料夾下,用ImageFolder提取相應路徑下的資料並設定bitch_size,考慮到VGG的輸入為224*224,所以要將圖片裁剪為224*224;提取資料長度dest_sizes,儲存類別資訊train_classes。
#組合多個函式 transform=transforms.Compose([ transforms.CenterCrop(224),#考慮到VGG的輸入為224*224,對原始圖片進行裁剪 #transforms.RandomHorizontalFlip(),#隨機左右翻轉 transforms.ToTensor(), transforms.Normalize(mean=[0.485,0.456,0.406],std=[0.229,0.224,0.225])])
#!ls "/content/cat_dog/train" #print(os.listdir()) #訓練時可以打亂順序增加多樣性,測試時沒有必要,所以shuffle=Falsedataset={x:datasets.ImageFolder(root=os.path.join('dogscats',x),transform=transform)forxin['train','val','test']} trainloader=torch.utils.data.DataLoader(dataset=dataset['train'],batch_size=64,shuffle=True,num_workers=6) valloader=torch.utils.data.DataLoader(dataset=dataset['val'],batch_size=64,shuffle=False,num_workers=6) testloader=torch.utils.data.DataLoader(dataset=dataset['test'],batch_size=64,shuffle=False,num_workers=6) dset_sizes={x:len(dataset[x])forxin['train','val','test']} train_classes=dataset['train'].classes
3、下載ImageNet的json檔案,使用訓練好的VGG16模型進行預測,最後用softmax進行處理。使用json.load()來讀取imagenet檔案,將輸入資料model後再softmax,展示識別結果,由於我的batch_size設定為64,圖片也就小了點,結果也就多了點。
!wget https://s3.amazonaws.com/deep-learning-models/image-models/imagenet_class_index.json model_vgg=models.vgg16(pretrained=True) #找到這個結果預測的是哪個類別,最簡單的可以找向量中最大的那個值,然後找出所對應的類別: with open('./imagenet_class_index.json') as f: #print(json.load(f)) class_dict = json.load(f) print(class_dict) dic_imagenet = [class_dict[str(i)][1] for i in range(len(class_dict))] inputs_try,labels_try = inputs_try.to(device),labels_try.to(device) model_vgg = model_vgg.to(device) outputs_try = model_vgg(inputs_try) print(outputs_try) print(outputs_try.shape)m_softm=nn.Softmax(dim=1) probs=m_softm(outputs_try) vals_try,pred_try=torch.max(probs,dim=1)
print('probsum:',torch.sum(probs,1)) print('vals_try:',vals_try) print('pred_try:',pred_try)
print([dic_imagenet[i]foriinpred_try.data]) imshow(torchvision.utils.make_grid(inputs_try.data.cpu()), title=[train_classes[x]forxinlabels_try.data.cpu()])
4、在這裡使用nn.Linear 層將結果的1000類替換為2類,model_vgg_new.parameters()是返回一個每次生成的是Tensor型別的資料的迭代器,設定 required_grad=False凍結前面層的引數。定義優化模型和損失函式。vgg.features是取出vgg16網路中的features大層。其中vgg網路可以分為3大層,一層是(features),一層是(avgpool),最後一層是(classifier),vgg.classifiwe._modules是將取出來的網路轉為字典顯示。
print(model_vgg)model_vgg_new=model_vgg;
forparaminmodel_vgg_new.parameters(): param.requires_grad=False model_vgg_new.classifier._modules['6']=nn.Linear(4096,2) model_vgg_new.classifier._modules['7']=torch.nn.LogSoftmax(dim=1)
model_vgg_new=model_vgg_new.to(device)
print(model_vgg_new.classifier) criterion=nn.NLLLoss() lr=0.001 #隨機梯度下降 optimizer_vgg=torch.optim.SGD(model_vgg_new.classifier[6].parameters(),lr=lr)
5、訓練模型
def train_model(model,dataloader,size,epochs,optimizer): model.train() for epoch in range(epochs): running_loss = 0.0 running_corrects = 0 count = 0 for inputs,classes in dataloader: inputs = inputs.to(device) classes = classes.to(device) outputs = model(inputs) loss = criterion(outputs,classes) optimizer = optimizer optimizer.zero_grad() loss.backward() optimizer.step() _,preds = torch.max(outputs.data,1) # statistics running_loss += loss.data.item() running_corrects += torch.sum(preds == classes.data) count += len(inputs) print('Training: No. ', count, ' process ... total: ', size) epoch_loss = running_loss / size epoch_acc = running_corrects.data.item() / size print('Loss: {:.4f} Acc: {:.4f}'.format(epoch_loss, epoch_acc)) # 模型訓練 train_model(model_vgg_new,trainloader,size=dset_sizes['train'], epochs=1, optimizer=optimizer_vgg)
訓練結果:
6、測試val
def test_model(model,dataloader,size): model.eval() predictions = np.zeros(size) all_classes = np.zeros(size) all_proba = np.zeros((size,2)) i = 0 running_loss = 0.0 running_corrects = 0 for inputs,classes in dataloader: inputs = inputs.to(device) classes = classes.to(device) outputs = model(inputs) loss = criterion(outputs,classes) _,preds = torch.max(outputs.data,1) # statistics running_loss += loss.data.item() running_corrects += torch.sum(preds == classes.data) predictions[i:i+len(classes)] = preds.to('cpu').numpy() all_classes[i:i+len(classes)] = classes.to('cpu').numpy() all_proba[i:i+len(classes),:] = outputs.data.to('cpu').numpy() i += len(classes) print('Testing: No. ', i, ' process ... total: ', size) epoch_loss = running_loss / size epoch_acc = running_corrects.data.item() / size print('Loss: {:.4f} Acc: {:.4f}'.format(epoch_loss, epoch_acc)) return predictions, all_proba, all_classes predictions, all_proba, all_classes = test_model(model_vgg_new,valloader,size=dset_sizes['val'])
測試結果:
7、測試test
predictions2,all_proba2,all_classes2=test_model(model_vgg_new,testloader,size=dset_sizes['test'])
測試結果:此處的0.4870不能看做是test的準確率,因為test裡面沒有cat和dog的標籤,所以這個數值是不準確的。
將圖片序號和預測結果匯入為csv檔案後再進行排序,在AI研習社的結果為:
8、遇到問題:
(1)匯入AI研習社下載連結後,使用unzip解壓失敗,針對.rar壓縮檔案應該使用unrar
(2)讀取檔案時出現找不到子檔案的錯誤,但是路徑格式我確定自己沒有填寫錯誤,後來發現ImageFolder的root針對的格式是這樣的:如果路徑是cat_dog/train/1.jpg,填寫的路徑要是cat_dog才是正確答案。我匯入的大賽的資料有三個資料夾train、test、val,每個資料夾下面沒有更多的資料夾,直接是圖片,這就導致按照正確格式讀取只能將三個資料夾的資料一起讀取。通過糾結考慮只有兩個辦法:①在每個資料夾下面分別建cat和dog資料夾,才能分別讀取train、test、val;②換個下載路徑。更換下載路徑後代碼正確。
(3)原始碼中訓練函式的引數是固定的,不利於後期的修改,尤其是optimizer沒有使用設定好的梯度下降優化函式
(4)下載csv提交時,出現正確率為50%的情況。原因是我沒有意識到圖片序號的重要性,直接按照圖片在test下的順序設定了range序列,這樣的結果是不正確的。
那為什麼結果是50%左右呢?由於test裡面沒有cat和dog的分類,所以分類的標籤為0,也就是說測試集中貓是正確,狗是錯誤,50%的意思是統計了貓在資料集中的數量佔比
9、優化
(1)提高訓練次數
(2)優化中