Basically that’s it (I made it simpler but the sense stayed unchanged):

def train(train_loader, model, optimizer, device):
model.train()
with tqdm(train_loader) as _tqdm:
    for x, y in _tqdm:
        x = x.to(device)
        y = y.to(device)
        outputs = model(x, y)
        loss = criterion(outputs, y)
        cur_loss = loss.item()
        sample_num = x.size(0)
        loss_monitor.update(cur_loss, sample_num)
        loss = loss_monitor.avg
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

return loss


def validate(val_loader, model, epoch, device):
model.eval()
with torch.no_grad():
    with tqdm(val_loader) as _tqdm:
        for x, y in _tqdm:
            x = x.to(device)
            outputs = model(x)
            # checking and saving output to calculate mae later
# I calculating mae ... works fine
return mae


def main():
start_epoch = 0
device = "cuda:0" if torch.cuda.is_available() else "cpu"
if device == torch.device("cuda:0"):
    cudnn.benchmark = True

val_dataset = LoadDataset("val")  # my own function to loading dataset - works fine
val_loader = DataLoader(
    val_dataset, batch_size=24, shuffle=False, num_workers=0
)
train_dataset = LoadDataset("train")
train_loader = DataLoader(
    train_dataset, batch_size=24, shuffle=False, num_workers=0
)

model = create_vgg_16()  # my own function which make a model based on vgg16- works fine
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
model = model.to(device)

best_val_mae = 100
num_epoch = 200

for epoch in range(start_epoch, num_epoch):

    train_loss = train(train_loader, model, optimizer, device)
    mae = validate(val_loader, model, epoch, device)

    # checkpoint
    if mae < best_val_mae:
        # saving model
        best_val_mae = mae



Looks like loss decreases normally.
I did it.
When during validation I use data from training problem is the same 
What do you mean by “stucking”?