kaggle竞赛-叶子分类

竞赛地址

最终正确率89左右 ,以后继续改进一下

导入相关并读取数据

1
2
3
4
5
6
7
8
9
10
11
12
13
import torch
import numpy as np
import pandas as pd
import os
from torch.utils.data import Dataset, DataLoader, TensorDataset
from torchvision import transforms,models
from PIL import Image
from torch import nn

train_file = './classify-leaves/train.csv'
test_file = './classify-leaves/test.csv'
img_file = './classify-leaves/'
train_df = pd.read_csv(train_file)

将树叶的类别由字符串转换为数字

1
2
3
4
5
#注意一定要sort一下,以确保每次运行的结果都是唯一的。因为set的结果不唯一
#也可以用train_df['label'].unique()
class_list = sorted(list(set(train_df['label'])))
num_to_class = dict(zip(range(len(class_list)), class_list))
class_to_num = {k:v for v,k in num_to_class.items()}

自定义dataset

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Mydata(Dataset):
def __init__(self, file_path, img_file, mode = 'train', train_weight = 0.8):
super().__init__()
self.img_file = img_file
data = pd.read_csv(file_path)
self.mode = mode
if mode == 'train':
self.len = int(len(data) * train_weight)
self.features = np.asarray(data.iloc[0:self.len,0])
self.labels = np.asarray(data.iloc[0:self.len,1])
elif mode == 'valid':
self.pos = int(len(data) * (train_weight))
self.features = np.asarray(data.iloc[self.pos:,0])
self.labels = np.asarray(data.iloc[self.pos:,1])
self.len = self.features.shape[0]
else:
self.len = len(data)
self.features = np.asarray(data.iloc[:,0])

def __getitem__(self, index):
img = Image.open(self.img_file + self.features[index])
if self.mode == 'train':
trans = transforms.Compose([
transforms.Resize((224,224)),
transforms.RandomHorizontalFlip(p=0.5),
transforms.ToTensor()
])
img = trans(img)
label = class_to_num[self.labels[index]]
return img, label
elif self.mode == 'valid':
trans = transforms.ToTensor()
img = trans(img)
label = class_to_num[self.labels[index]]
return img, label
else:
trans = transforms.ToTensor()
img = trans(img)
return img

def __len__(self):
return self.len

定义dataloder

1
2
3
4
5
6
7
8
9
10
11
train_data = Mydata(train_file, img_file, 'train')
valid_data = Mydata(train_file, img_file , 'valid')
test_data = Mydata(test_file, img_file, 'test')
batch_size = 64
train_iter = DataLoader(train_data, batch_size=batch_size, shuffle=True)
valid_iter = DataLoader(valid_data, batch_size=batch_size, shuffle=True)
test_iter = DataLoader(test_data, batch_size=batch_size, shuffle=False)

#X需要是float32类型,不能是double。 y需要是int64(long)类型
X,y = next(iter(train_iter))
X.dtype, y.dtype

网络模型及超参数

1
2
3
4
5
6
7
8
def get_net():
net = models.resnet34(pretrained=True)
net.fc = nn.Linear(512, len(class_list))
return net
#学习率调低一些
learning_rate = 3e-4
weight_decay = 1e-3
num_epochs = 50

训练

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
net = get_net()
net = net.to(device)

loss_func = nn.CrossEntropyLoss()
loss_func = loss_func.to(device)

optimizer = torch.optim.Adam(net.parameters(), lr=learning_rate,weight_decay=weight_decay)
best_acc = 0.0
for epoch in range(num_epochs):
print('----------第{}轮次---------'.format(epoch + 1))
net.train()
train_ls = []
train_acc = []
for (X,y) in train_iter:
X = X.to(device)
y = y.to(device)
y_hat = net(X)
loss = loss_func(y_hat, y)
acc = (y_hat.argmax(dim=1) == y).sum() / len(y)

train_ls.append(loss)
train_acc.append(acc)

optimizer.zero_grad()
loss.backward()
optimizer.step()

acc = sum(train_acc) / len(train_acc)
loss = sum(train_ls) / len(train_ls)
print(f'[train {epoch+1:3d} / {num_epochs:3d}] acc = {acc:.3f}, loss = {loss:.5f}')


net.eval()
valid_ls = []
valid_acc = []
for (X,y) in valid_iter:
X = X.to(device)
y = y.to(device)
with torch.no_grad():
y_hat = net(X)
loss = loss_func(y_hat, y)
acc = (y_hat.argmax(dim=1) == y).sum() / len(y)

valid_ls.append(loss)
valid_acc.append(acc)
acc = sum(valid_acc) / len(valid_acc)
loss = sum(valid_ls) / len(valid_ls)

print(f"[ Valid | {epoch + 1:3d}/{num_epochs:3d} ] loss = {loss:.5f}, acc = {acc:.5f}")

if acc > best_acc:
best_acc = acc
torch.save(net.state_dict(), 'net.pth')
print('第{}轮次保存了模型'.format(epoch+1))

预测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
net = get_net()
net.to(device)
net.load_state_dict(torch.load('../working/net.pth'))
net.eval()
with torch.no_grad():
pre = []
for X in test_iter:
X = X.to(device)
y_hat = net(X)
pre.extend(y_hat.argmax(1).cpu().numpy().tolist())
ans = []
for i in pre:
ans.append(num_to_class[i])

test_data = pd.read_csv(test_file)
test_data['label'] = pd.Series(ans)
submission = pd.concat([test_data['image'], test_data['label']], axis=1)
submission.to_csv('submission.csv', index=False)

其他

1
2
3
#将Image转换为np,注意输出维度的变化,224*224*3
arr = np.array(Imgae.open('.jpg'))
arr.shape