问题描述
我在 PyTorch 中训练了一个 CNN 模型来检测 6 个不同类别的皮肤病。我的模型的准确率为 92%,我将它保存在一个 .pth 文件中。我想使用这个模型进行预测,但我不知道如何去做。如果有人可以帮助我完成必要的步骤,我将不胜感激。
我曾尝试直接从文件夹中获取图像输入,调整其大小,然后通过模型运行以进行预测。我面临的错误是 ModuleAttributeAError ,它表示没有名为 predict 的属性。现在我不明白我哪里出错了,我知道这对大多数人来说是一项简单的任务,但我希望在这方面得到一些指导。我使用的数据集是来自 kaggle 的皮肤癌 MNIST:HAM10000 数据集,并在 resnet18 上对其进行了训练。如果有人对模型微调有任何建议,我将不胜感激。
TLDR:我收到一个名为 ModuleAttributeError 的错误,指出“resnet”模块没有“预测”属性。
这里对图片进行了如下预处理:
import os,cv2,itertools
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import pickle
from tqdm import tqdm
from glob import glob
from PIL import Image
# pytorch libraries
import torch
from torch import optim,nn
from torch.autograd import Variable
from torch.utils.data import DataLoader,Dataset
from torchvision import models,transforms
# sklearn libraries
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
np.random.seed(10)
torch.manual_seed(10)
torch.cuda.manual_seed(10)
print(os.listdir("/content/drive/My Drive/input"))
from google.colab import drive
drive.mount('/content/drive')
"""**Data analysis and preprocessing**"""
data_dir = '/content/drive/My Drive/input'
all_image_path = glob(os.path.join(data_dir,'*','*.jpg'))
imageid_path_dict = {os.path.splitext(os.path.basename(x))[0]: x for x in all_image_path}
lesion_type_dict = {
'nv': 'Melanocytic nevi','mel': 'Melanoma','bkl': 'Benign keratosis-like lesions ','bcc': 'Basal cell carcinoma','akiec': 'Actinic keratoses','vasc': 'Vascular lesions','df': 'Dermatofibroma'
}
def compute_img_mean_std(image_paths):
"""
computing the mean and std of three channel on the whole dataset,first we should normalize the image from 0-255 to 0-1
"""
img_h,img_w = 224,224
imgs = []
means,stdevs = [],[]
for i in tqdm(range(len(image_paths))):
img = cv2.imread(image_paths[i])
img = cv2.resize(img,(img_h,img_w))
imgs.append(img)
imgs = np.stack(imgs,axis=3)
print(imgs.shape)
imgs = imgs.astype(np.float32) / 255.
for i in range(3):
pixels = imgs[:,:,i,:].ravel() # resize to one row
means.append(np.mean(pixels))
stdevs.append(np.std(pixels))
means.reverse() # BGR --> RGB
stdevs.reverse()
print("normMean = {}".format(means))
print("normStd = {}".format(stdevs))
return means,stdevs
# norm_mean,norm_std = compute_img_mean_std(all_image_path)
norm_mean = (0.763035,0.54564625,0.5700399)
norm_std = (0.1409281,0.15261264,0.16997051)
df_original = pd.read_csv(os.path.join(data_dir,'HAM10000_Metadata.csv'))
df_original['path'] = df_original['image_id'].map(imageid_path_dict.get)
df_original['cell_type'] = df_original['dx'].map(lesion_type_dict.get)
df_original['cell_type_idx'] = pd.Categorical(df_original['cell_type']).codes
df_original.head()
# this will tell us how many images are associated with each lesion_id
df_undup = df_original.groupby('lesion_id').count()
# Now we filter out lesion_id's that have only one image associated with it
df_undup = df_undup[df_undup['image_id'] == 1]
df_undup.reset_index(inplace=True)
df_undup.head()
# here we identify lesion_id's that have duplicate images and those that have only one image.
def get_duplicates(x):
unique_list = list(df_undup['lesion_id'])
if x in unique_list:
return 'unduplicated'
else:
return 'duplicated'
# create a new colum that is a copy of the lesion_id column
df_original['duplicates'] = df_original['lesion_id']
# apply the function to this new column
df_original['duplicates'] = df_original['duplicates'].apply(get_duplicates)
df_original.head()
df_original['duplicates'].value_counts()
# Now we filter out images that don't have duplicates
df_undup = df_original[df_original['duplicates'] == 'unduplicated']
df_undup.shape
# Now we create a val set using df because we are sure that none of these images have augmented duplicates in the train set
y = df_undup['cell_type_idx']
_,df_val = train_test_split(df_undup,test_size=0.2,random_state=101,stratify=y)
df_val.shape
df_val['cell_type_idx'].value_counts()
# This set will be df_original excluding all rows that are in the val set
# This function identifies if an image is part of the train or val set.
def get_val_rows(x):
# create a list of all the lesion_id's in the val set
val_list = list(df_val['image_id'])
if str(x) in val_list:
return 'val'
else:
return 'train'
# identify train and val rows
# create a new colum that is a copy of the image_id column
df_original['train_or_val'] = df_original['image_id']
# apply the function to this new column
df_original['train_or_val'] = df_original['train_or_val'].apply(get_val_rows)
# filter out train rows
df_train = df_original[df_original['train_or_val'] == 'train']
print(len(df_train))
print(len(df_val))
df_train['cell_type_idx'].value_counts()
df_val['cell_type'].value_counts()
# copy fewer class to balance the number of 7 classes
data_aug_rate = [15,10,5,50,40,5]
for i in range(7):
if data_aug_rate[i]:
df_train=df_train.append([df_train.loc[df_train['cell_type_idx'] == i,:]]*(data_aug_rate[i]-1),ignore_index=True)
df_train['cell_type'].value_counts()
# # We can split the test set again in a validation set and a true test set:
# df_val,df_test = train_test_split(df_val,test_size=0.5)
df_train = df_train.reset_index()
df_val = df_val.reset_index()
# df_test = df_test.reset_index()
这里是我构建模型的地方:
# feature_extract is a boolean that defines if we are finetuning or feature extracting.
# If feature_extract = False,the model is finetuned and all model parameters are updated.
# If feature_extract = True,only the last layer parameters are updated,the others remain fixed.
def set_parameter_requires_grad(model,feature_extracting):
if feature_extracting:
for param in model.parameters():
param.requires_grad = False
def initialize_model(model_name,num_classes,feature_extract,use_pretrained=True):
# Initialize these variables which will be set in this if statement. Each of these
# variables is model specific.
model_ft = None
input_size = 0
if model_name == "resnet":
""" resnet18,resnet34,resnet50,resnet101
"""
model_ft = models.resnet18(pretrained=use_pretrained)
set_parameter_requires_grad(model_ft,feature_extract)
num_ftrs = model_ft.fc.in_features
model_ft.fc = nn.Linear(num_ftrs,num_classes)
input_size = 224
elif model_name == "vgg":
""" VGG11_bn
"""
model_ft = models.vgg11_bn(pretrained=use_pretrained)
set_parameter_requires_grad(model_ft,feature_extract)
num_ftrs = model_ft.classifier[6].in_features
model_ft.classifier[6] = nn.Linear(num_ftrs,num_classes)
input_size = 224
elif model_name == "densenet":
""" Densenet121
"""
model_ft = models.densenet121(pretrained=use_pretrained)
set_parameter_requires_grad(model_ft,feature_extract)
num_ftrs = model_ft.classifier.in_features
model_ft.classifier = nn.Linear(num_ftrs,num_classes)
input_size = 224
elif model_name == "inception":
""" Inception v3
"""
model_ft = models.inception_v3(pretrained=use_pretrained)
set_parameter_requires_grad(model_ft,feature_extract)
# Handle the auxilary net
num_ftrs = model_ft.AuxLogits.fc.in_features
model_ft.AuxLogits.fc = nn.Linear(num_ftrs,num_classes)
# Handle the primary net
num_ftrs = model_ft.fc.in_features
model_ft.fc = nn.Linear(num_ftrs,num_classes)
input_size = 299
else:
print("Invalid model name,exiting...")
exit()
return model_ft,input_size
# resnet,vgg,densenet,inception
model_name = 'resnet'
num_classes = 7
feature_extract = False
# Initialize the model for this run
model_ft,input_size = initialize_model(model_name,use_pretrained=True)
# Define the device:
device = torch.device('cuda:0')
# Put the model on the device:
model = model_ft.to(device)
# norm_mean = (0.49139968,0.48215827,0.44653124)
# norm_std = (0.24703233,0.24348505,0.26158768)
# define the transformation of the train images.
train_transform = transforms.Compose([transforms.Resize((input_size,input_size)),transforms.RandomHorizontalFlip(),transforms.RandomVerticalFlip(),transforms.Randomrotation(20),transforms.ColorJitter(brightness=0.1,contrast=0.1,hue=0.1),transforms.ToTensor(),transforms.normalize(norm_mean,norm_std)])
# define the transformation of the val images.
val_transform = transforms.Compose([transforms.Resize((input_size,norm_std)])
# Define a pytorch DataLoader for this dataset
class HAM10000(Dataset):
def __init__(self,df,transform=None):
self.df = df
self.transform = transform
def __len__(self):
return len(self.df)
def __getitem__(self,index):
# Load data and get label
X = Image.open(self.df['path'][index])
y = torch.tensor(int(self.df['cell_type_idx'][index]))
if self.transform:
X = self.transform(X)
return X,y
# Define the training set using the table train_df and using our defined transitions (train_transform)
training_set = HAM10000(df_train,transform=train_transform)
train_loader = DataLoader(training_set,batch_size=64,shuffle=True,num_workers=4)
# Same for the validation set:
validation_set = HAM10000(df_val,transform=train_transform)
val_loader = DataLoader(validation_set,shuffle=False,num_workers=4)
# we use Adam optimizer,use cross entropy loss as our loss function
optimizer = optim.Adam(model.parameters(),lr=1e-5)
criterion = nn.CrossEntropyLoss().to(device)
最后是带有预测函数的训练过程:
# this function is used during training process,to calculation the loss and accuracy
class AverageMeter(object):
def __init__(self):
self.reset()
def reset(self):
self.val = 0
self.avg = 0
self.sum = 0
self.count = 0
def update(self,val,n=1):
self.val = val
self.sum += val * n
self.count += n
self.avg = self.sum / self.count
total_loss_train,total_acc_train = [],[]
def train(train_loader,model,criterion,optimizer,epoch):
model.train()
train_loss = AverageMeter()
train_acc = AverageMeter()
curr_iter = (epoch - 1) * len(train_loader)
for i,data in enumerate(train_loader):
images,labels = data
N = images.size(0)
# print('image shape:',images.size(0),'label shape',labels.size(0))
images = Variable(images).to(device)
labels = Variable(labels).to(device)
optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs,labels)
loss.backward()
optimizer.step()
prediction = outputs.max(1,keepdim=True)[1]
train_acc.update(prediction.eq(labels.view_as(prediction)).sum().item()/N)
train_loss.update(loss.item())
curr_iter += 1
if (i + 1) % 100 == 0:
print('[epoch %d],[iter %d / %d],[train loss %.5f],[train acc %.5f]' % (
epoch,i + 1,len(train_loader),train_loss.avg,train_acc.avg))
total_loss_train.append(train_loss.avg)
total_acc_train.append(train_acc.avg)
return train_loss.avg,train_acc.avg
def validate(val_loader,epoch):
model.eval()
val_loss = AverageMeter()
val_acc = AverageMeter()
with torch.no_grad():
for i,data in enumerate(val_loader):
images,labels = data
N = images.size(0)
images = Variable(images).to(device)
labels = Variable(labels).to(device)
outputs = model(images)
prediction = outputs.max(1,keepdim=True)[1]
val_acc.update(prediction.eq(labels.view_as(prediction)).sum().item()/N)
val_loss.update(criterion(outputs,labels).item())
print('------------------------------------------------------------')
print('[epoch %d],[val loss %.5f],[val acc %.5f]' % (epoch,val_loss.avg,val_acc.avg))
print('------------------------------------------------------------')
return val_loss.avg,val_acc.avg
import cv2
from PIL import Image,ImageOps
import numpy as np
model = model_ft
model.load_state_dict(torch.load("/content/drive/MyDrive/input/trainbest.pth"))
model.eval()
def import_and_predict(image_data,model):
size = (224,224)
image = ImageOps.fit(image_data,size,Image.ANTIALIAS)
img = np.asarray(image)
image_reshape = img[np.newaxis,...]
prediction = model.predict(img_reshape)
return prediction
image = Image.open('/content/0365-0596-abd-88-05-0712-gf03.jpg')
# st.image(image,use_column_width = True)
predictions = import_and_predict(image,model)
class_names = ["Melanocytic nevi","dermatofibroma","Benign keratosis-like lesions","Basal cell carcinoma","Actinic keratoses","Vascular lesions","Dermatofibroma"]
string = "It is: " + class_names[np.argmax(predictions)]
print(string)
这是执行后立即出现的错误。
---------------------------------------------------------------------------
ModuleAttributeError Traceback (most recent call last)
<ipython-input-219-d563271b78c6> in <module>()
32 image = Image.open('/content/0365-0596-abd-88-05-0712-gf03.jpg')
33 # st.image(image,use_column_width = True)
---> 34 predictions = import_and_predict(image,model)
35 class_names = ["Melanocytic nevi","Dermatofibroma"]
36 string = "It is: " + class_names[np.argmax(predictions)]
1 frames
<ipython-input-219-d563271b78c6> in import_and_predict(image_data,model)
27 img = np.asarray(image)
28 image_reshape = img[np.newaxis,...]
---> 29 prediction = model.predict(img_reshape)
30 return prediction
31
/usr/local/lib/python3.6/dist-packages/torch/nn/modules/module.py in __getattr__(self,name)
777 return modules[name]
778 raise ModuleAttributeError("'{}' object has no attribute '{}'".format(
--> 779 type(self).__name__,name))
780
781 def __setattr__(self,name: str,value: Union[Tensor,'Module']) -> None:
ModuleAttributeError: 'resnet' object has no attribute 'predict'
如果有人能帮我解决这个问题并让它作为皮肤病的分类器工作,我会非常感激。
解决方法
nn.Module
没有 predict
函数,只需调用对象进行推理:
prediction = model(img_reshape)
这将调用对象的 __call__
函数,而后者又调用模型的 forward
函数。