You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

106 lines
4.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import torch
from NCF.dataloader import Construct_DataLoader
def pick_optimizer(network, params):
optimizer = None
if params['optimizer'] == 'sgd':
optimizer = torch.optim.SGD(network.parameters(),
lr=params['sgd_lr'],
momentum=params['sgd_momentum'],
weight_decay=params['l2_regularization'])
elif params['optimizer'] == 'adam':
optimizer = torch.optim.Adam(network.parameters(),
lr=params['adam_lr'],
weight_decay=params['l2_regularization'])
elif params['optimizer'] == 'rmsprop':
optimizer = torch.optim.RMSprop(network.parameters(),
lr=params['rmsprop_lr'],
alpha=params['rmsprop_alpha'],
momentum=params['rmsprop_momentum'])
return optimizer
class Trainer(object):
def __init__(self, model, config):
self._config = config
self._model = model
# 选择优化器
self._optimizer = pick_optimizer(self._model, self._config)
# 定义损失函数,对于隐反馈数据,这里使用交叉熵损失函数
self._crit = torch.nn.BCELoss()
def _train_single_batch(self, users, items, ratings):
"""
对单个小批量数据进行训练
:param users: user Tensor
:param items: item Tensor
:param ratings: rating Tensor
:return:
"""
if self._config['use_cuda'] is True:
# 将这些数据由CPU迁移到GPU
users, items, ratings = users.cuda(), items.cuda(), ratings.cuda()
# 先将梯度清零,如果不清零那么这个梯度就和上一个mini-batch有关
self._optimizer.zero_grad()
# 模型的输入users items调用forward进行前向传播
ratings_pred = self._model(users, items)
# 通过交叉熵损失函数来计算损失, ratings_pred.view(-1)代表将预测结果摊平,变成一维的结构。
loss = self._crit(ratings_pred.view(-1), ratings)
# 反向传播计算梯度
loss.backward()
# 梯度下降等优化器 更新参数
self._optimizer.step()
# 将loss的值提取成python的float类型
loss = loss.item()
return loss
def _train_an_epoch(self, train_loader, epoch_id):
"""
训练一个Epoch即将训练集中的所有样本全部都过一遍
:param train_loader: Torch的DataLoader
:param epoch_id: 训练轮次Id
:return:
"""
# 告诉模型目前处于训练模式启用dropout以及batch normalization
self._model.train()
total_loss = 0
# 从DataLoader中获取小批量的id以及数据
for batch_id, batch in enumerate(train_loader):
assert isinstance(batch[0], torch.LongTensor)
# 这里的user, item, rating大小变成了1024维了因为batch_size是1024即每次选取1024个样本数据进行训练
user, item, rating = batch[0], batch[1], batch[2]
rating = rating.float()
loss = self._train_single_batch(user, item, rating)
print('[Training Epoch {}] Batch {}, Loss {}'.format(epoch_id, batch_id, loss))
total_loss += loss
print('Training Epoch: {}, TotalLoss: {}'.format(epoch_id, total_loss))
def train(self, sampleGenerator):
# 是否使用GPU加速
self.use_cuda()
# 是否使用预先训练好的参数
self.load_preTrained_weights()
for epoch in range(self._config['num_epoch']):
print('-' * 20 + ' Epoch {} starts '.format(epoch) + '-' * 20)
# 每个轮次都重新随机产生样本数据集
users, items, ratings = sampleGenerator(num_negatives=self._config['num_negative'])
# 构造一个DataLoader
data_loader = Construct_DataLoader(users=users, items=items, ratings=ratings,
batchsize=self._config['batch_size'])
# 训练一个轮次
self._train_an_epoch(data_loader, epoch_id=epoch)
def use_cuda(self):
if self._config['use_cuda'] is True:
assert torch.cuda.is_available(), 'CUDA is not available'
torch.cuda.set_device(self._config['device_id'])
self._model.cuda()
def load_preTrained_weights(self):
if self._config['pretrain'] is True:
self._model.load_preTrained_weights()
def save(self):
self._model.saveModel()