Kaggle入门比赛:灾难推文的自然语言处理 详细教程

Kaggle入门比赛:灾难推文的NLP 详细教程

最近对NLP挺感兴趣,打算学习一下。在这里记录一下学习过程和中途遇到的一些坑!!
ps:下文中贴出的都是一些代码块,就个人经验而言自己手敲一边发现bug并debug的过程可以大大增强对数据结构和pytorch框架的认识。

代码放在个人github上:https://github.com/JYJ0327/Kaggle-conpetition

运行环境:用jupyter notebook运行
使用pytorch框架进行训练

1.数据预处理

1)检查数据并进行空白填充

从Kaggle网站下载灾难推文数据集后,是一个csv文件,先检查一下数据,可以看到训练集数据共7613行,5列,分别是id,location,keyword,text与target,其中,部分样本存在keyword与location缺失,对缺失部分进行填充。

#数据处理均在notebook上进行
#读取数据集
pd.set_option('display.width', 1000)#
df_train = pd.read_csv('./df_train.csv')
#对null进行填充
for df in [df_train, df_test]:
    for col in ['keyword', 'location']:
        df[col] = df[col].fillna(f'no_{col}')
 df_train

在这里插入图片描述

2)数据清理

数据清理主要是使用正则表达式对文本的符号的tokenizer无法识别的字符进行清理和替换。
ps:下面的代码只展示了如何处理,大家可以将其封装成函数对原本进行处理后保存为数据集中新的一列

    # Urls
    tweet = re.sub(r"https?:\/\/t.co\/[A-Za-z0-9]+", "", tweet)
        
    # Words with punctuations and special characters
    punctuations = '@#!?+&*[]-%.:/();$=><|{}^' + "'`"
    for p in punctuations:
        tweet = tweet.replace(p, f' {p} ')
        
    # ... and ..
    tweet = tweet.replace('...', ' ... ')
    if '...' not in tweet:
        tweet = tweet.replace('..', ' ... ')      
        
    # Acronyms
    tweet = re.sub(r"MH370", "Malaysia Airlines Flight 370", tweet)
    tweet = re.sub(r"m̼sica", "music", tweet)
    tweet = re.sub(r"okwx", "Oklahoma City Weather", tweet)
    tweet = re.sub(r"arwx", "Arkansas Weather", tweet)    
    tweet = re.sub(r"gawx", "Georgia Weather", tweet)  
    tweet = re.sub(r"scwx", "South Carolina Weather", tweet)  
    tweet = re.sub(r"cawx", "California Weather", tweet)
    tweet = re.sub(r"tnwx", "Tennessee Weather", tweet)
    tweet = re.sub(r"azwx", "Arizona Weather", tweet)  
    tweet = re.sub(r"alwx", "Alabama Weather", tweet)
    tweet = re.sub(r"wordpressdotcom", "wordpress", tweet)    
    tweet = re.sub(r"usNWSgov", "United States National Weather Service", tweet)
    tweet = re.sub(r"Suruc", "Sanliurfa", tweet)   
    
    # Grouping same words without embeddings
    tweet = re.sub(r"Bestnaijamade", "bestnaijamade", tweet)
    tweet = re.sub(r"SOUDELOR", "Soudelor", tweet)

3)分词

简单的数据清洗之后就可以使用分词器对文本进行向量化了,只有将词转换为向量才能送入神经网络进行学习。这里推荐使用transformers库,可以很方便的使用预训练的神经网络模型与对应的分词器。

#加载分词器
model_name = 'bert-base-uncased'
tokenizer = BertTokenizer.from_pretrained(pretrained_model_name_or_path=model_name)

sentences = df_train['text_cleaned'] + df_train['keyword']
sentences = sentences.tolist()
sentences_tokenizer = tokenizer(sentences,
                                truncation=True,
                                padding=True,
                                max_length=128,
                                add_special_tokens=True)

因为后续的训练在GPU上进行,所以这里直接将数据转化为tensor形式保存。(这里建议大家讲数据预处理和训练过程分开,将预处理完的数据保存为npy格式,这样可以避免后续训练搜索参数时重复进行数据预处理,浪费时间

sentences_tokenizer['input_ids'] = np.array(sentences_tokenizer['input_ids'])
sentences_tokenizer['attention_mask'] = np.array(sentences_tokenizer['attention_mask'])
#del sentences_tokenizer['token_type_ids']
data = {}
data['input_ids'] = torch.tensor(sentences_tokenizer['input_ids'])
data['attention_mask'] = torch.tensor(sentences_tokenizer['attention_mask'])
data['labels'] = torch.tensor(df_train['target'])
#保存数据
np.save('sentences_tokenizer.npy', data)

2.训练过程

1)读取保存好的tensor类型数据

data_tensor = np.load('sentences_tokenizer.npy',allow_pickle=True)
for key in data_tensor.item():
    data_tensor.item()[key] = data_tensor.item()[key].to(devices)

2)重构本次训练的Dataset类与Module类

class DisasterDataset(Dataset):
    def __init__(self,data):
        self.data = data.item()

    def __getitem__(self, idx):
        labels = self.data['labels'][idx]
        sentences = {}
        sentences['input_ids'] = self.data['input_ids'][idx]
        sentences['attention_mask'] = self.data['attention_mask'][idx]
        
        return sentences, labels
    def __len__(self):
        return len(self.data['input_ids'])

class BertClassificationModel(nn.Module):
    def __init__(self,dropout,hidden_size=768):
        super(BertClassificationModel, self).__init__()
        
        model_name = 'bert-base-uncased'

        # 读取预训练模型
        self.bert = BertModel.from_pretrained(pretrained_model_name_or_path=model_name)
#        self.bert = BertForSequenceClassification.from_pretrained(pretrained_model_name_or_path=model_name, num_labels=2)
#        for p in self.bert.parameters(): # 冻结bert参数
#                p.requires_grad = False
        self.dropout = nn.Dropout(dropout, inplace=True)
        self.fc = nn.Linear(hidden_size,2)

    def forward(self, batch_sentences):   # [batch_size,1]
        
        input_ids = batch_sentences['input_ids']
        attention_mask = batch_sentences['attention_mask']
        bert_out=self.bert(input_ids=input_ids,attention_mask=attention_mask) # 模型
        last_hidden_state =bert_out[0] # [batch_size, sequence_length, hidden_size] # 变量
        bert_cls_hidden_state=last_hidden_state[:,0,:] # 变量
        self.dropout(bert_cls_hidden_state)
        fc_out=self.fc(bert_cls_hidden_state) # 模型
        return fc_out          

ps:这里我取消了冻结预训练bert的参数,大家实验时可以冻结观察实验结果的不同。

3)搭建训练过程

def main():
    batchsize = 256  # 定义每次放多少个数据参加训练
    #这里大家可以根据自己显卡情况调整
    Datas = DisasterDataset(data_tensor) # 加载训练集
    train_data, val_data = torch.utils.data.random_split(Datas,[6144,1469])
   # train_loader = torch.utils.data.DataLoader(Datas, batch_size=batchsize, shuffle=False)#遍历train_dataloader 每次返回batch_size条数据
    val_loader = torch.utils.data.DataLoader(val_data, batch_size=batchsize, shuffle=False)
    #在调参数可以将训练集分为训练集和验证集,最后确定参数数需要将所有训练集都用于训练

    # 这里搭建训练循环,输出训练结果
    epoch_num = 2 
    dropout = 0.3
    # 初始化模型
    model=BertClassificationModel(dropout=dropout)
    if isinstance(model,torch.nn.DataParallel):
        model = model.module
    model = nn.DataParallel(model).to(devices)#这里用单机多卡并行,如果只有单卡可以不用这个
    optimizer = optim.AdamW(model.parameters(), lr=2e-5) 
   # scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=2,gamma=0.4 )

    criterion = nn.CrossEntropyLoss()
    criterion.to(devices)
    
    print("模型数据已经加载完成,现在开始模型训练。")
    for epoch in tqdm(range(epoch_num)):
        model.train()
        acc = 0
        for i, (data,labels) in enumerate(train_loader, 0):

            output = model(data)
            optimizer.zero_grad()  # 梯度清0
            loss = criterion(output, labels)  # 计算误差
            out = output.argmax(dim=1)
            acc += (out == labels).sum().item()
            
            loss.backward()  # 反向传播
            optimizer.step()  # 更新参数
        scheduler.step()
            # 打印一下每一次数据扔进去学习的进展
#            print('batch:%d loss:%.5f' % (i, loss.item()))
        
        # 打印一下每个epoch的深度学习的进展i
        train_acc = acc / 6144
        print('epoch:%d acc:%.5f' % (epoch, train_acc))
    
 #   torch.save(model.state_dict(),'model.pth')#最后一次训练时记得保存模型
        num = 0
     model.eval()  # 不启用 BatchNormalization 和 Dropout,保证BN和dropout不发生变化,主要是在测试场景下使用;

     for j, (data,labels) in enumerate(test_loader, 0):

         output = model(data)
         out = output.argmax(dim=1)
         num += (out == labels).sum().item()
         val_acc = num / 1469
         print('val_Accuracy:', val_acc)


3.测试并提交

1)读取处理过的测试集

ps:测试集的处理与训练集相同

data_tensor = np.load('sentences_tokenizer_test.npy',allow_pickle=True)
for key in data_tensor.item():
    data_tensor.item()[key] = data_tensor.item()[key].to(devices)

2)重构训练集Dataset类

ps:因为训练集数据没有label,所以需要重新构建,这里其实也可以通过一个判断和训练集构建成同一个,但因为一开始自己学的时候没想到0.0

class DisasterDataset(Dataset):
    def __init__(self,data):
        self.data = data.item()

    def __getitem__(self, idx):
        sentences = {}
        sentences['input_ids'] = self.data['input_ids'][idx]
        sentences['attention_mask'] = self.data['attention_mask'][idx]
        
        return sentences
    def __len__(self):
        return len(self.data['input_ids'])

3)加载训练好的模型

model = BertClassificationModel(0.3)
model = nn.DataParallel(model).to(devices)
model.load_state_dict(torch.load('model.pth'))

ps:这里因为保存模型时保存的知识模型的参数,所以需要定义一个结构相同的模型实例后,再将参数传入

4)对测试集进行推理

batchsize = 128  # 定义每次放多少个数据参加训练
    
Datas = DisasterDataset(data_tensor) # 加载训练集
test_loader = torch.utils.data.DataLoader(Datas, batch_size=batchsize, shuffle=False)#遍历train_dataloader 每次返回batch_size条数据

model.eval()  # 不启用 BatchNormalization 和 Dropout,保证BN和dropout不发生变化,主要是在测试场景下使用;
label = []
for j, data in enumerate(test_loader, 0):
    #data = data.to(devices)
    output = model(data)
    out = output.argmax(dim=1)
    out=out.tolist()
    label.extend(out)

5)保存并提交结果

label = pd.DataFrame(label)
ids = pd.read_csv('test.csv')#这里因为测试集的id不是连续的!!!所以需要读取一下
ids = ids.iloc[:,1]
label = pd.concat([ids,label],axis=1)
fieldname = ['id','target']
label.to_csv("submission.csv",index_label = fieldname)
  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Kaggle NLP是指在Kaggle平台上进行自然语言处理(NLP)相关比赛的活动。在Kaggle上的NLP比赛中,参与者利用文本数据来解决各种语言处理问题,例如文本分类、情感分析、命名实体识别等。 过去,参与Kaggle NLP比赛需要进行复杂的文本预处理工作,如词袋模型(Bag of Words)等。然而,随着BERT(Bidirectional Encoder Representations from Transformers)的出现,文本预处理变得不再那么重要了。BERT是一种基于Transformer模型的预训练语言模型,它在NLP任务上取得了显著的成绩。使用BERT或其后继者作为模型的基础,参与Kaggle NLP比赛变得简单许多。此外,Kaggle平台上提供免费的GPU资源,进一步降低了参与比赛的门槛。 因此,参与Kaggle的NLP比赛现在变得更加容易,对于想要入坑NLP比赛的人来说,有现成的开源框架和免费的GPU资源可供使用。通过学习和应用各种技巧和经验,参与者可以在Kaggle上取得优秀的成绩。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [新手入门Kaggle NLP比赛总结](https://blog.csdn.net/Datawhale/article/details/105236480)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值