如今各类应用的推荐系统做的越来越完善,推荐主要基于用户信息的准确分析,其中性别和年龄预测通常占据重要的地位,那么如何从用户行为中准确分析出性别和年龄?传统的机器学习算法例如逻辑回归,需要人工建构特征进行输入训练,而基于神经网络的模型可以自动提取特征,对于有着大量训练数据的任务来说是一个很不错的选择。
使用Pytorch构建深度模型,基本按照以下流程展开:
由于数据的私有性,不可以公开,因此我们假设数据原型如下(假设与年龄和性别密切相关的字段数据,有昵称、偏好、其它):
昵称 | 偏好 | 其它 |
---|---|---|
可爱的小花 | 收集的足迹信息 | 补充信息(e.g. 小红书打开频率更高) |
王大拿 | 收集的足迹信息 | 补充信息 |
冷血无情 | 收集的足迹信息 | 补充信息 |
以上数据为应用内收集信息,需要对数据进行向量化处理,才可以作为神经网络的输入。通常有这样两种转换方式:
① 原始数据本身为数字类型数据,那么可以将其进行归一化后直接作为特征进行输入;
② 文字类数据需要做相应的转换,可以使用one-hot码或者使用word2vec转换为特征向量(个人建议,如果该做法不正确,请指出~);
数据输入网络模型通常会使用自定义的Dataloader类,通过继承Dataset类重写__init__、__getitem__以及__len__方法。
class AgePredictData(Dataset):
def __init__(self, csv_path=None, train=True):
# 通常初始化数据源
self.data = pd.read_csv(csv_path, header=None)
self.train_data = self.data.iloc[0:820000, :] # 切分训练集 82W向量
self.test_data = self.data.iloc[820000: 1000000, :]
self.info = self.data.describe().shape
self.train = train
def test(self):
return self.train_data if self.train else self.test_data
def transform(self, train, index):
# 将数据转换为Tensor格式(以下代码是任务相关的,具体任务需要具体修改)
if train:
# Series to np array
data = pd.Series.to_numpy(self.train_data.iloc[index, 0: (self.info[1] - 5)]).reshape(400)
label = np.array([self.train_data.iloc[index, -5:]]).reshape(5)
else:
data = pd.Series.to_numpy(self.test_data.iloc[index, 0: (self.info[1] - 5)]).reshape(400)
label = pd.Series.to_numpy(self.test_data.iloc[index, -5:]).reshape(5)
data = torch.from_numpy(data).float()
label = torch.from_numpy(label).float()
return data, label
def __getitem__(self, index):
# 获取一条输入数据
data_, label_ = self.transform(self.train, index)
return data_, label_
def __info__(self):
return self.info # column count
def __len__(self):
# 返回数据的长度
return len(self.train_data) if self.train else len(self.test_data)
对于一般的数据,可以直接使用全连接网络 Full Connection Network
。隐藏层个数可以通过手动调整。
def add_module(self, module):
self.add_module(str(len(self) + 1), module)
torch.nn.Module.add = add_module
def fcn(num_input_channels, num_output_channels, num_hidden):
# Fully Connected Network, fcn
model = nn.Sequential()
model.add(nn.Linear(num_input_channels, num_hidden, bias=True))
model.add(nn.Dropout(p=0.1))
model.add(nn.ReLU6())
model.add(nn.Linear(num_hidden, num_output_channels))
# model.add(nn.Softmax(dim=1))
return model
net = fcn().to(device)
mse = torch.nn.MSELoss().to(device) # 损失函数选用均方误差损失或其它损失
optimizer = torch.optim.SGD([{'params': net.parameters()}], lr=LR) # 优化器选择随机梯度下降或其它优化器
def train():
net.train()
logging.info("start to Train ...")
for step in range(EPOCH):
for i_batch, (x, y) in enumerate(train_loader):
x = x.to(device)
y = y.to(device)
y_ = net(x) # net output
loss = mse(y_, torch.max(y, 1)[1])
optimizer.zero_grad()
loss.backward()
optimizer.step()
if i_batch % 1000 == 0:
print("current epoch: ", step + 1, "/", EPOCH, ", the i_batch is ", i_batch, ", the loss is %f" % loss)
logging.warning("save model parameters to 'model/%d_net_params.pkl' " % step)
torch.save(net.state_dict(), 'model/' + str(step) + '_net_params.pkl') # 保存网络参数
测试结果仅仅计算了准确率。然后在模型的评价标准中,准确率并不能说明模型的好坏。
def test():
correct = torch.zeros(1).squeeze()
# load model
logging.info("start to TEST, loading model ...")
net.load_state_dict(torch.load('model/90_net_params.pkl'))
logging.info("loaded model, calculating ...")
for i_batch, (x, y) in enumerate(test_loader):
x = x.to(device)
y_ = net(x) # .cpu().detach().squeeze().numpy()
prediction = torch.argmax(y_, dim=1).cpu()
labels = torch.argmax(y, dim=1)
correct += (prediction == labels).sum().float()
print("the test accuracy is %f" % (correct.numpy() / test_data.__len__() * 100), "%")
如果需要计算查全率查准率,可以使用sklearn工具包。
如果以上表述存在问题,欢迎留言~完整代码参见我的github。