求问,为什么我这个验证的准确率一直不变
收藏
我用vgg模型,训练图像分类,但是验证集的acc居然一直不变!求教,我错在哪里了?
数据集是眼疾识别数据集
项目:https://aistudio.baidu.com/aistudio/projectdetail/2190247?shared=1
需要把data的validation解压到work下面
代码:
import cv2
import random
import numpy as np
import os
import paddle
from paddle.nn import Conv2D, MaxPool2D, Linear, Dropout, BatchNorm2D, Softmax
import paddle.nn.functional as F
from visualdl import LogWriter
# 设置日志保存路径
log_writer = LogWriter("./work/log")
DATADIR = 'work/PALM-Training400/PALM-Training400'
DATADIR2 = 'work/PALM-Validation400'
CSVFILE = 'work/labels.csv'
# 对读入的图像进行预处理
def transform_img(img):
img = cv2.resize(img, (224, 224))
# 读入图像的数据格式是[H,W,C]
# 使用转置操作使其变成[C,H,W]
img = np.transpose(img, (2, 0, 1)).astype('float32')
# 将数据范围调整到[-1.0, 1.0]之间
img = img/255.
img = img*2.0 - 1.0
return img
# 定义训练集数据读取器
def data_loader(datadir, batchsize=10, mode='train'):
filenames = os.listdir(datadir)
def reader():
if mode == 'train':
# 训练时数据乱序
random.shuffle(filenames)
batch_imgs = []
batch_labels = []
for name in filenames:
filepath = os.path.join(datadir, name)
img = cv2.imread(filepath)
img = transform_img(img)
if name[0] == 'H' or name[0] == 'N':
# H开头的文件名表示高度近似,N开头的文件名表示正常视力
# 高度近视和正常视力的样本,都不是病理性的,属于负样本,标签为0
label = 0
elif name[0] == 'P':
# P开头的是病理性近视,属于正样本,标签为1
label = 1
else:
raise ('Not support file name')
label = np.reshape(label, [1])
# 每读取一个样本的数据,就将其放入数据列表中
batch_imgs.append(img)
batch_labels.append(label)
if len(batch_imgs) == batchsize:
imgs_array = np.array(batch_imgs).astype('float32')
labels_array = np.array(batch_labels).astype('int64')
yield imgs_array, labels_array
batch_imgs = []
batch_labels = []
if len(batch_imgs) > 0:
imgs_array = np.array(batch_imgs).astype('float32')
labels_array = np.array(batch_labels).astype('int64')
yield imgs_array, labels_array
return reader
# 定义验证集数据读取器
def valid_data_loader(datadir, csvfile, batch_size=10, mode='valid'):
# 训练集读取时通过文件名来确定样本标签,验证集则通过csvfile来读取每个图片对应的标签
# 请查看解压后的验证集标签数据,观察csvfile文件里面所包含的内容
# csvfile文件所包含的内容格式如下,每一行代表一个样本,
# 其中第一列是图片id,第二列是文件名,第三列是图片标签,
# 第四列和第五列是Fovea的坐标,与分类任务无关
# ID,imgName,Label,Fovea_X,Fovea_Y
# 1,V0001.jpg,0,1157.74,1019.87
# 2,V0002.jpg,1,1285.82,1080.47
# 打开包含验证集标签的csvfile,并读入其中的内容
file_lists = open(csvfile).readlines()
def reader():
batch_imgs = []
batch_labels = []
for line in file_lists[1:]:
line = line.strip().split(',')
name = line[1]
# print(line)
label = int(line[2])
label = np.reshape(label, [1])
# 根据图片文件名加载图片,并对图像数据作预处理
file_path = os.path.join(datadir, name)
img = cv2.imread(file_path)
img = transform_img(img)
# 每读取一个样本的数据,就将其放入数据列表中
batch_imgs.append(img)
batch_labels.append(label)
if len(batch_imgs) == batch_size:
imgs_array = np.array(batch_imgs).astype('float32')
labels_array = np.array(batch_labels).astype('int64')
yield imgs_array, labels_array
# 清空数据读取列表
batch_imgs = []
batch_labels = []
if len(batch_imgs) > 0:
imgs_array = np.array(batch_imgs).astype('float32')
labels_array = np.array(batch_labels).astype('int64')
yield imgs_array, labels_array
return reader
class VGG(paddle.nn.Layer):
def __init__(self, num_classes=1):
super(VGG, self).__init__()
in_channels = [3, 64, 128, 256, 512, 512]
# 定义第一个卷积块,包含两个卷积
self.conv1_1 = Conv2D(in_channels=in_channels[0], out_channels=in_channels[1], kernel_size=3, padding=1, stride=1)
self.conv1_2 = Conv2D(in_channels=in_channels[1], out_channels=in_channels[1], kernel_size=3, padding=1, stride=1)
# 定义第二个卷积块,包含两个卷积
self.conv2_1 = Conv2D(in_channels=in_channels[1], out_channels=in_channels[2], kernel_size=3, padding=1, stride=1)
self.conv2_2 = Conv2D(in_channels=in_channels[2], out_channels=in_channels[2], kernel_size=3, padding=1, stride=1)
# 定义第三个卷积块,包含三个卷积
self.conv3_1 = Conv2D(in_channels=in_channels[2], out_channels=in_channels[3], kernel_size=3, padding=1, stride=1)
self.conv3_2 = Conv2D(in_channels=in_channels[3], out_channels=in_channels[3], kernel_size=3, padding=1, stride=1)
self.conv3_3 = Conv2D(in_channels=in_channels[3], out_channels=in_channels[3], kernel_size=3, padding=1, stride=1)
# 定义第四个卷积块,包含三个卷积
self.conv4_1 = Conv2D(in_channels=in_channels[3], out_channels=in_channels[4], kernel_size=3, padding=1, stride=1)
self.conv4_2 = Conv2D(in_channels=in_channels[4], out_channels=in_channels[4], kernel_size=3, padding=1, stride=1)
self.conv4_3 = Conv2D(in_channels=in_channels[4], out_channels=in_channels[4], kernel_size=3, padding=1, stride=1)
# 定义第五个卷积块,包含三个卷积
self.conv5_1 = Conv2D(in_channels=in_channels[4], out_channels=in_channels[5], kernel_size=3, padding=1, stride=1)
self.conv5_2 = Conv2D(in_channels=in_channels[5], out_channels=in_channels[5], kernel_size=3, padding=1, stride=1)
self.conv5_3 = Conv2D(in_channels=in_channels[5], out_channels=in_channels[5], kernel_size=3, padding=1, stride=1)
# 使用Sequential 将全连接层和relu组成线性结构(fc+relu)
# 当输入为224*224时,经过5个卷积块和池化层后,形状变为512*7*7
self.fc1 = paddle.nn.Sequential(
paddle.nn.Linear(512*7*7, 4096),
paddle.nn.ReLU()
)
self.drop1_ratio = 0.5
self.dropout1 = paddle.nn.Dropout(self.drop1_ratio, mode='upscale_in_train')
# 使用Sequential将全连接层和relu组成一个线性结构(fc+relu)
self.fc2 = paddle.nn.Sequential(
paddle.nn.Linear(4096, 4096),
paddle.nn.ReLU()
)
self.drop2_ratio = 0.5
self.dropout2 = paddle.nn.Dropout(self.drop2_ratio, mode='upscale_in_train')
self.fc3 = paddle.nn.Linear(4096, num_classes)
self.relu = paddle.nn.ReLU()
self.pool = MaxPool2D(stride=2, kernel_size=2)
self.softmax = Softmax()
def forward(self, x, label=None):
x = self.relu(self.conv1_1(x))
x = self.relu(self.conv1_2(x))
x = self.pool(x)
x = self.relu(self.conv2_1(x))
x = self.relu(self.conv2_2(x))
x = self.pool(x)
x = self.relu(self.conv3_1(x))
x = self.relu(self.conv3_2(x))
x = self.relu(self.conv3_3(x))
x = self.pool(x)
x = self.relu(self.conv4_1(x))
x = self.relu(self.conv4_2(x))
x = self.relu(self.conv4_3(x))
x = self.pool(x)
x = self.relu(self.conv5_1(x))
x = self.relu(self.conv5_2(x))
x = self.relu(self.conv5_3(x))
x = self.pool(x)
x = paddle.flatten(x, 1, -1)
x = self.dropout1(self.relu(self.fc1(x)))
x = self.dropout2(self.relu(self.fc2(x)))
x = self.fc3(x)
# x = self.softmax(x)
if label is not None:
# print(x)
# print(label)
acc = paddle.metric.accuracy(input=x, label=label)
return x, acc
else:
return x
# 定义训练过程
def train_pm(model, optimizer):
# 开启0号GPU训练
use_gpu = False
paddle.set_device('gpu:0') if use_gpu else paddle.set_device('cpu')
print("start training...")
model.train()
epoch_num = 10
# 定义数据读取器,训练数据读取器和验证数据读取器
train_loader = data_loader(DATADIR, batchsize=10, mode='train')
valid_loader = valid_data_loader(DATADIR2, CSVFILE)
iter = 0
iters = []
for epoch in range(epoch_num):
for batch_id, data in enumerate(train_loader()):
x_data, y_data = data
img = paddle.to_tensor(x_data)
label = paddle.to_tensor(y_data)
# 前向计算
logits, acc = model(img, label)
loss = F.cross_entropy(logits, label)
avg_loss = paddle.mean(loss)
if batch_id % 10 == 0:
# 使用visual DL进行绘图
iters.append(iter)
log_writer.add_scalar(tag='acc', step=iter, value=acc.numpy())
log_writer.add_scalar(tag='loss', step=iter, value=avg_loss.numpy())
print('epoch:{}, batch_id:{}, loss is:{}'.format(epoch, batch_id, avg_loss.numpy()))
iter += 1
# 反向传播,更新权重,清除梯度
avg_loss.backward()
optimizer.step()
optimizer.clear_grad()
model.eval()
accuracies = []
losses = []
for batch_id, data in enumerate(valid_loader()):
x_data, y_data = data
img = paddle.to_tensor(x_data)
label = paddle.to_tensor(y_data)
logits, acc = model(img, label)
loss = F.cross_entropy(input=logits, label=label)
avg_val_loss = paddle.mean(loss)
accuracies.append(float(acc.numpy()))
losses.append(float(avg_val_loss.numpy()))
# 计算多个batch的平均损失和准确率
acc_val_mean = np.array(accuracies).mean()
avg_loss_val_mean = np.array(losses).mean()
log_writer.add_scalar(tag='eval_acc', step=iter, value=acc_val_mean)
print("loss={}, acc={}".format(avg_loss_val_mean, acc_val_mean))
model.train()
paddle.save(model.state_dict(), './work/VGG.pdparams')
# 创建模型
model = VGG(num_classes=2)
# 启动训练过程
# opt = paddle.optimizer.Momentum(learning_rate=0.001, momentum=0.9, parameters=model.parameters())
opt = paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters())
train_pm(model, optimizer=opt)
0
收藏
请登录后评论
从日志上看只有两个epoch没有变?
由于交叉熵损失函数自带了Softmax激活函数,如果在组网阶段额外添加了Softmax激活函数,这样会变成两个Softmax,进而影响模型收敛。
同样,在二分类任务当中,不建议使用较高的学习率+Adam优化器高维优化方案,建议使用SGD优化器或减少学习率来避免无法正确拟合。
解决方案:
1. 删除组网阶段的Softmax。
2. 使用SGD优化器/更低的学习率。
问下 楼主 怎么解决的,我也遇到了验证集一直不变的情况,怎么改都没用。。。
降低学习率即可