Добавил:
Кафедра ВТ Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Проект / НС_проект_отчёт.docx
Скачиваний:
3
Добавлен:
07.04.2023
Размер:
1.22 Mб
Скачать

Демонстрация работы архитектуры

Создание датасета:

# зададим какие преобразования необходимо сделать с каждым изображением

batch_size=32 #кол-во изображений загружаемых за раз

transform = transforms.Compose(

[transforms.Resize((224,224)), #изменим размер изображений

transforms.ToTensor(), #переведем в формат который необходим нейронной сети - тензор

transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])]) # проведем нормализацию изображения

trainset = torchvision.datasets.ImageFolder(train_dir, transform=transform)

trainloader = torch.utils.data.DataLoader(trainset, batch_size=batch_size, num_workers=2, shuffle=True)

testset = torchvision.datasets.ImageFolder(test_dir, transform=transform)

testloader = torch.utils.data.DataLoader(testset, batch_size=batch_size, num_workers=2, shuffle=True)

classes = list(set(list(csv_file["Class"])))

def imshow(img):

img = img / 2 + 0.5

npimg = img.numpy()

plt.imshow(np.transpose(npimg, (1, 2, 0)))

plt.show()

dataiter = iter(trainloader)

images, labels = next(dataiter)

imshow(torchvision.utils.make_grid(images))

print(' '.join(f'{classes[labels[j]]}' for j in range(batch_size)))

Класс собственной архитектуры:

class Net4(nn.Module):

def __init__(self):

super(Net4,self).__init__()

self.relu = nn.ReLU()

self.conv1 = nn.Conv2d(3, 8, kernel_size =3, stride =1, padding = 0)

self.conv2 = nn.Conv2d(8, 16, kernel_size =3, stride =1, padding= 1)

self.conv3 = nn.Conv2d(16, 32, kernel_size =3, stride =1, padding= 2)

self.conv4 = nn.Conv2d(32, 64, kernel_size =3, stride =1, padding= 3)

self.maxpool = nn.MaxPool2d(2, 2)

self.fc1 = nn.Linear(16384, len(classes))

# Это forward функция, которая определяет структуру сети.

# Здесь мы принимаем только один вход, но можно использовать больше.

def forward(self, x):

x = self.conv1(x)

x = self.relu(x)

x = self.maxpool(x)

x = self.conv2(x)

x = self.relu(x)

x = self.maxpool(x)

x = self.conv3(x)

x = self.relu(x)

x = self.maxpool(x)

x = self.conv4(x)

x = self.maxpool(x)

x = x.view(x.size(0),-1)

x = self.fc1(x)

return torch.log_softmax(x, dim=1)

Мы хотели сделать легковесную модель на основе VGG11.

Обучение с помощью cnn

Мы пытались протестировать различные архитектуры на основе свёрточных нейронных сетей (CNN), но, к сожалению, они давали крайне неудовлетворительный результат.

Мы предполагали, что присваивание меток должно начинаться не с 1, а с 0, вследствие чего мы поменяли значения в CSV-файле. Однако данный способ также не помог увеличить результат. В итоге, мы пришли к выводу, что использование DataLoader без ImageFolder для выборок увеличивает точность модели.

Почему мы используем функцию потерь именно “CrossEntropyLoss”, и оптимизатор “Adam”? С функцией оптимизации “Adam” мы знакомы по практике и по лабораторным работам, а “CrossEntropyLoss” мы используем потому, что она использует как активацию softmax, так и отрицательную логарифмическую вероятность потери.

Код обучающего класса:

from torch.autograd import Variable

# Модифицируем (видимо, не вперый раз) вспомогательный класс под текущую задачу

class Optimization:

"""Optimization is a helper class that allows training, validation, prediction.

Optimization is a helper class that takes model, loss function, optimizer function

learning scheduler (optional), early stopping (optional) as inputs. In return, it

provides a framework to train and validate the models, and to predict future values

based on the models.

Attributes:

model (nn.Module): Model class

loss_fn (torch.nn.modules.Loss): Loss function to calculate the losses

optimizer (torch.optim.Optimizer): Optimizer function to optimize the loss function

train_losses (list[float]): The loss values from the training

val_losses (list[float]): The loss values from the validation

"""

def __init__(self, model, loss_fn, optimizer):

"""

Args:

model (nn.Module): Model class

loss_fn (torch.nn.modules.Loss): Loss function to calculate the losses

optimizer (torch.optim.Optimizer): Optimizer function to optimize the loss function

"""

self.model = model

self.loss_fn = loss_fn

self.optimizer = optimizer

self.train_losses = []

self.val_losses = []

self.err_rates = []

def train_step(self, x, y) -> "loss":

"""The method train_step completes one step of training.

Given the features (x) and the target values (y) tensors, the method completes

one step of the training. First, it activates the train mode to enable back prop.

After generating predicted values (yhat) by doing forward propagation, it calculates

the losses by using the loss function. Then, it computes the gradients by doing

back propagation and updates the weights by calling step() function.

Args:

x (torch.Tensor): Tensor for features to train one step

y (torch.Tensor): Tensor for target values to calculate losses

"""

# Sets model to train mode

self.model.train() # dropout is here, so call it

# Makes predictions

yhat = self.model(x)

# Computes loss

loss = self.loss_fn(yhat.logits, y) # change y hat.logits to y.hat

# Computes gradients

loss.backward()

# Updates parameters and zeroes gradients

self.optimizer.step()

self.optimizer.zero_grad()

# Returns the loss

return loss.item()

def train(self, train_loader, val_loader, n_epochs, test_loader = None) -> None:

"""The method train performs the model training

The method takes DataLoaders for training and validation datasets, batch size for

mini-batch training, number of epochs to train.

Then, it carries out the training by iteratively calling the method train_step for

n_epochs times. If early stopping is enabled, then it checks the stopping condition

to decide whether the training needs to halt before n_epochs steps. Finally, it saves

the model in a designated file path.

Args:

train_loader (torch.utils.data.DataLoader): DataLoader that stores training data

val_loader (torch.utils.data.DataLoader): DataLoader that stores validation data

n_epochs (int): Number of epochs, i.e., train steps, to train

test_loader (torch.utils.data.DataLoader): set it if need error_rate after one epoch

"""

model_path = f'model_{datetime.datetime.now().strftime("%Y_%m_%d_%H_%M_%S")}'

start_time_gl = time.time()

for epoch in range(1, n_epochs + 1):

start_time_ep = time.time()

gi, N = 0, len(train_loader)

batch_losses = []

print(f"({epoch}/{n_epochs}) {N}: ", end="")

start_time_bt = time.time()

for x_batch, y_batch in train_loader:

x_batch = x_batch.to(device)

y_batch = y_batch.to(device)

loss = self.train_step(x_batch, y_batch)

batch_losses.append(loss)

gi+=1

print(f"_{gi}", end="")

print(f" ({round(time.time() - start_time_bt, 2)} s)")

training_loss = np.mean(batch_losses)

self.train_losses.append(training_loss)

with torch.no_grad():

batch_val_losses = []

for x_val, y_val in val_loader:

x_val = x_val.to(device)

y_val = y_val.to(device)

self.model.eval()

yhat = self.model(x_val)

val_loss = self.loss_fn(yhat, y_val).item()

batch_val_losses.append(val_loss)

validation_loss = np.mean(batch_val_losses)

self.val_losses.append(validation_loss)

print(f"({epoch}/{n_epochs}) Training loss: {training_loss:.4f}\t Validation loss: {validation_loss:.4f}\t", end ="")

print(f" ({round(time.time() - start_time_ep, 2)} s)")

print(f"Train time: ({round(time.time() - start_time_gl, 2)} s)")

torch.save(self.model.state_dict(), model_path)

def evaluate(self, test_loader) -> "(predicted, values)":

"""The method evaluate performs the model evaluation

The method takes DataLoaders for the test dataset, batch size for mini-batch testing,

and number of features as inputs. Similar to the model validation, it iteratively

predicts the target values and calculates losses. Then, it returns two lists that

hold the predictions and the actual values.

Note:

This method assumes that the prediction from the previous step is available at

the time of the prediction, and only does one-step prediction into the future.

Args:

test_loader (torch.utils.data.DataLoader): DataLoader that stores test data

Returns:

list[float]: The values predicted by the model

list[float]: The actual values in the test set.

"""

with torch.no_grad():

predictions = []

values = []

for x_test, y_test in test_loader:

x_test = x_test.to(device)

y_test = y_test.to(device)

self.model.eval()

yhat = self.model(x_test)

predictions.append(yhat.to("cpu").detach().numpy())

values.append(y_test.to("cpu").detach().numpy())

return predictions, values

def check_adequacy(self, test_loader) -> None:

start_time = time.time()

# correct_pred = {classname: 0 for classname in CarsDS.get_classes_list()}

# total_pred = {classname: 0 for classname in CarsDS.get_classes_list()}

correct_pred, total_pred = 0, 0

preds, reals = self.evaluate(test_loader)

for pred_i, real_i in zip(preds, reals):

for pred_i_j, real_i_j in zip(pred_i, real_i):

pred_i_j_one = np.argmax(pred_i_j)

if(pred_i_j_one == real_i_j):

#correct_pred[CarsDS.convert_tensor_to_labels([real_i_j])[0]] += 1

correct_pred += 1

total_pred += 1

#total_pred[CarsDS.convert_tensor_to_labels([real_i_j])[0]] += 1

# accuracy_one_u = 0

# accuracy_one_d = 0

# for classname, correct_count in correct_pred.items():

# if (total_pred[classname] == 0):

# accuracy = 0

# else:

# accuracy = 100 * float(correct_count) / total_pred[classname]

# accuracy_one_u += correct_count

# accuracy_one_d += total_pred[classname]

# print(f'Accuracy for class: {classname:7d} is {accuracy:.1f} %')

#print(f'Accuracy: {100.0*accuracy_one_u/accuracy_one_d:.1f} %')

print(f'Accuracy: {100.0*correct_pred/total_pred:.1f} %')

print(f"Test time: ({round(time.time() - start_time, 2)} s)")

def find_loss(model, valid_loader, lr_min, lr_max, steps=50) -> None:

weight_decay = 0

#dataiter = iter(testloader)

#xs, ys = next(dataiter)

#xs, ys = xs.to(device), ys.to(device)

i, N = 0, steps

X, Y = [], []

print(f"{N}: ", end="")

start_time = time.time()

for i in range(N+1):

cur_lr = lr_min + (lr_max-lr_min)*(i/N)

loss_fn = nn.CrossEntropyLoss()

optimizer = optim.Adam(model.parameters(), lr=cur_lr, weight_decay=weight_decay)

opt = Optimization(model=model, loss_fn=loss_fn, optimizer=optimizer)

#loss = opt.train_step(xs, ys)

loss = 0

for x_batch, y_batch in valid_loader:

x_batch = x_batch.to(device)

y_batch = y_batch.to(device)

loss_l = opt.train_step(x_batch, y_batch)

loss += loss_l

print(f"_{i+1}", end="")

X.append(cur_lr)

Y.append(loss)

print(f" ({round(time.time() - start_time, 2)} s)")

plt.plot(X, Y)

#print(f"X={X}, Y={Y}")

df = pd.DataFrame(data={"lr": X, "loss": Y})

with pd.option_context('display.max_rows', None, 'display.max_columns', None):

print(df)

def plot_losses(self):

"""The method plots the calculated loss values for training and validation

"""

plt.plot(self.train_losses, label="Training loss")

plt.plot(self.val_losses, label="Validation loss")

if(len(self.err_rates) > 0):

plt.plot(self.err_rates, label="Error rate")

plt.legend()

plt.title("Losses")

plt.show()

plt.close()

Соседние файлы в папке Проект