大家好,我是小伍哥,很久没更新内容风控相关的内容了,今天在写一篇。文本数据的处理,对于一个风控策略或者算法,我觉得是必须要掌握的技能,有人说,我的风控并不涉及到文本?我觉得这片面了,在非内容风控领域,文本知识也是非常有用的。
用户昵称、地址啥的,这种绝大部分风控场景都能遇到,如果要做互联网风控,对文本的处理基本上是必不可少的技能。20大风控文本分类算法 系列,已经写了5篇,介绍了风控场景下文本分类的基本方法,对抗文本变异,包括传统的词袋模型、循环神经网络,也有常用于计算机视觉任务的卷积神经网络,以及 RNN + CNN,试验完一遍,基本能搞定大部分的文本分类以及文本变异对抗问题。
今天是第 6 讲,主要讲BERT进行文本特征的应用。使用BERT模型,绕不开HuggingFace提供的Transformers生态。HuggingFace提供了各类BERT的API(transformers库)、训练好的模型(HuggingFace Hub)还有数据集(datasets)。Transformers库提供了很多简单易用的接口,使开发者能够使用和训练各种预训练的NLP模型,例如BERT、GPT、RoBERTa等。这些模型在各种NLP任务上表现出色,包括文本分类、命名实体识别、情感分析等。
我们的目标是创建一个模型,该模型以句子为输入(就如上述数据集中的评论),输出为1(句子带有积极情感)或者0(句子带有消极情感)。如下图所示:
以下是HuggingFace目前提供的类列表
BertModel BertForPreTraining BertForMaskedLM BertForNextSentencePrediction(下句预测) BertForSequenceClassification(我们本次使用的分类模型) BertForTokenClassification BertForQuestionAnswering
一、模型安装
二、模型探索
1、模型加载
import torch
import random
from transformers import BertTokenizer, BertForSequenceClassification, AdamW
from torch.utils.data import DataLoader, Dataset, random_split
import pandas as pd
from tqdm import tqdm
# 加载BERT 和 分词器
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
model = BertForSequenceClassification.from_pretrained('bert-base-chinese'
# 获取字典的长度
dictionary = tokenizer.get_vocab()
len(dictionary)
# 查看模型结构
print(model)
print(tokenizer)
BertTokenizer(name_or_path='bert-base-chinese', vocab_size=21128,
model_max_length=512, is_fast=False, padding_side='right',
truncation_side='right',
special_tokens={'unk_token': '[UNK]', 'sep_token': '[SEP]', 'pad_token': '[PAD]', 'cls_token': '[CLS]', 'mask_token': '[MASK]'},
clean_up_tokenization_spaces=True
)
2、句子分词
# 切词结果
token = tokenizer.tokenize('小伍哥真帅')
print(token)
['小', '伍', '哥', '真', '帅']
3、词转换成ID
# 将词转换为对应字典的id
indexes = tokenizer.convert_tokens_to_ids(token)
print(indexes) # 输出id
[2207, 824, 1520, 4696, 2358]
# 将id转换为对应字典的词
tokens = tokenizer.convert_ids_to_tokens(indexes)
print(tokens) # 输出词
['小', '伍', '哥', '真', '帅']
# 用tokenizer.encode编码,会自动加上[CLS]和[SEP]
input_encoded = tokenizer.encode('小伍哥真帅')
input_encoded
[101, 2207, 824, 1520, 4696, 2358, 102]
#用tokenizer.decode接码还原
tokenizer.decode(input_encoded)
'[CLS] 小 伍 哥 真 帅 [SEP]'
input_encoded = tokenizer('小伍哥真帅')
print(input_encoded)
{'input_ids': [101, 2207, 824, 1520, 4696, 2358, 102],
'token_type_ids': [0, 0, 0, 0, 0, 0, 0],
'attention_mask': [1, 1, 1, 1, 1, 1, 1]
}
tokenizer.decode(input_encoded["input_ids"])
'[CLS] 小 伍 哥 真 帅 [SEP]'
input_encoded = tokenizer('小伍哥真帅','小伍哥正真有钱')
print(input_encoded)
{'input_ids': [101, 2207, 824, 1520, 4696, 2358, 102, 2207, 824, 1520, 3633, 4696, 3300, 7178, 102],
'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1],
'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}
input_encoded = tokenizer(['小伍哥真帅','小伍哥正真的太有钱'], padding=True, return_tensors="pt")
input_encoded
{'input_ids':
tensor([[ 101, 2207, 824, 1520, 4696, 2358, 102, 0, 0, 0, 0],
[ ]]),
'token_type_ids':
tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ ]]),
'attention_mask':
tensor([[1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
[ ]])}
4、提取流程
5、词向量对比
import torch
from transformers import BertTokenizer, BertModel
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
model = BertModel.from_pretrained("bert-base-chinese")
input_ids1 = torch.tensor(tokenizer.encode("大米粥")).unsqueeze(0) # Batch size 1
input_ids2 = torch.tensor(tokenizer.encode("小米手机")).unsqueeze(0) # Batch size 1
outputs1 = model(input_ids1)
outputs2 = model(input_ids2)
print(outputs1[0][0][2]) # 大米粥中的 米
print(outputs2[0][0][2]) # 小米手机中的 米
五、BERT分类实战
1、数据读取
# 读取训练数据集
#df = pd.read_csv("weibo_senti_100k.csv")
df = pd.read_csv("text_all.csv")
# 随机打乱数据行
df = df.sample(frac=1).reset_index(drop=True)
# 微博数据集 查看数据
df.head()
label review
0 1 @?子的歌 的老爸吹瓶吹得好欢乐啊![笑哈哈]//@微美食厦门: #2013微美食厦门专属达...
1 0 被shootme鄙视了【failed starting server. please che...
2 0 本来不想评论1:5的,因为实在是已经习惯了!但今天中午吃面条时,边上一老大妈居然都在聊:“哎...
3 0 俭姐肿么办//@果粒橙_cyx:美的你,非要上帅哥的车,把成绩都影响了[抓狂]
4 0 请大家帮忙 //@莎墨:还是@小影客 发现告诉我们的[晕] //@双鱼座的KIKI:今天早上...
# 弹幕数据集 查看数据
label review
0 1 2591 1478寂寞美女口..
1 0 加速器免费,加速部分要收费
2 0 开抽vx了,小姐姐ᚠ
3 1 恭 CC-名字-看拼ᚰ
4 1 __𝙝𝖈593ᚰ盘片
5 0 刚才的双飞呢
6 0 吼哦哦哦
7 1 染口口 29521 91783
8 0 人家直接无视我
9 0 小哥哥好嫩
# 读取训练数据集 两个数据都可以测试下
df = pd.read_csv("weibo_senti_100k.csv")
df = pd.read_csv("text_all.csv")
# 加载模型和分词器
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
model = BertForSequenceClassification.from_pretrained('bert-base-chinese')
# 如果下不了 也可以离线加载模型
# model_path = '/Users/wuzhengxiang/Documents/BERT/bert-base-chinese'
# tokenizer = BertTokenizer.from_pretrained(model_path)
# model = BertForSequenceClassification.from_pretrained(model_path)
token = tokenizer.tokenize("佳喂:sx111505可越ne")
token
'佳', '喂', ':', 's', '##x', '##11', '##150', '##5', '可', '越', 'ne'] [
# 设置随机种子以确保可重复性
random.seed(42)
# 随机打乱数据行
df = df.sample(frac=1).reset_index(drop=True)
# 数据集中1为正样本或者黑样本,0为负样本或者白样本
class SentimentDataset(Dataset):
def __init__(self, dataframe, tokenizer, max_length=128):
self.dataframe = dataframe
self.tokenizer = tokenizer
self.max_length = max_length
def __len__(self):
return len(self.dataframe)
def __getitem__(self, idx):
text = self.dataframe.iloc[idx]['review']
label = self.dataframe.iloc[idx]['label']
encoding = self.tokenizer(text, padding='max_length', truncation=True, max_length=self.max_length, return_tensors='pt')
return {
'input_ids': encoding['input_ids'].flatten(),
'attention_mask': encoding['attention_mask'].flatten(),
'labels': torch.tensor(label, dtype=torch.long)
}
# 创建数据集对象
dataset = SentimentDataset(df[:1500], tokenizer)
3、训练集、验证集划分
# 创建数据集对象
dataset = SentimentDataset(df[:1500], tokenizer)
# 划分训练集和验证集
train_size = int(0.8 * len(dataset))
val_size = len(dataset) - train_size
train_dataset, val_dataset = random_split(dataset, [train_size, val_size])
# 创建数据加载器,batch_size定义每次训练的数据量
train_loader = DataLoader(train_dataset, batch_size=8, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=8, shuffle=False)
4、参数设置
# 设置训练参数
optimizer = AdamW(model.parameters(), lr=5e-5)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
5、模型训练
# 训练模型
model.train()
for epoch in range(3): # 3个epoch作为示例
for batch in tqdm(train_loader, desc="Epoch {}".format(epoch + 1)):
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
labels = batch['labels'].to(device)
# 正向传播
optimizer.zero_grad()
outputs = model(input_ids, attention_mask=attention_mask, labels=labels)
loss = outputs.loss
# 反向梯度信息
loss.backward()
# 参数更新
optimizer.step()
6、模型评估
# 评估模型
model.eval()
total_eval_accuracy = 0
for batch in tqdm(val_loader, desc="Evaluating"):
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
labels = batch['labels'].to(device)
with torch.no_grad():
outputs = model(input_ids, attention_mask=attention_mask)
logits = outputs.logits
preds = torch.argmax(logits, dim=1)
accuracy = (preds == labels).float().mean()
total_eval_accuracy += accuracy.item()
average_eval_accuracy = total_eval_accuracy / len(val_loader)
Accuracy:", average_eval_accuracy)
7、模型预测
# 使用微调后的模型进行预测
def predict_sentiment(sentence):
inputs = tokenizer(sentence, padding='max_length', truncation=True, max_length=128, return_tensors='pt').to(device)
with torch.no_grad():
outputs = model(**inputs)
logits = outputs.logits
probs = torch.softmax(logits, dim=1)
positive_prob = probs[0][1].item() # 1表示正面
negtived_prob = probs[0][0].item() # 0表示负面
print("黑样本概率:", positive_prob,"白样本概率:", negtived_prob)
# 找几个案例测试下
predict_sentiment("资源口扣12 5656 8844")
黑样本概率: 0.9945248365402222 白样本概率: 0.005475129000842571
# 买课加我微信 wuzhx2014
predict_sentiment("买课加我微信 wuzhx2014")
黑样本概率: 0.9529826641082764 白样本概率: 0.04701732099056244
SynchroTrap-基于松散行为相似度的欺诈账户检测算法