赞
踩
本文将回顾第四届“中国法研杯”司法人工智能挑战赛-刑期预测算法赛道比赛。使用多任务预训练、然后进行微调的形式最终在比赛中取得了三等奖的成绩。
主办方在第一届“中国法研杯”比赛上提出了刑期预测任务,本届将针对往届刑期预测准确率不高的罪名进行专项研究,并提供更多维度信息(如省份、年份)进行帮助提升。
本任务技术评测使用的训练集、验证集、测试集由中国司法大数据院提供,包含大约40余万篇裁判文书的犯罪事实、本院认为、刑期、年份、省份信息。
刑期分布:在给出的数据集中,刑期分布在1~234个月区间内,且不是连续的,因此在建模时本文没有使用回归预测的方法,而是使用文本分类的方法。
案由分布:从下图可见,案由分布极不均衡。
数据样例
在比赛过程中,本文收集了往年的法研杯比赛数据集,通过正则等手段清洗获得了与本次比赛案由相同的数据,进行多任务预训练。
往年数据清洗过滤后的数据样例,该数据用于多任务预训练:
{
"caseCause": "故意伤害罪",
"justice": "贵州省平坝县人民检察院指控:2014年4月9日下午,被告人王某丁与其堂哥王4某(另案处理)假释驾驶大货车(贵×××××)准备到乐某镇大屯村拉砂,行至大屯村时与被害人王某乙相遇,王4某与王某乙因琐事发生矛盾,准备打架时被路人劝开,双方离开现场。之后,王4某打电话邀约王某乙打架,接着又打电话召集郑3某、艾1某雷、邱某(均另案处理)、。王3某王6某、王某丁与艾1某雷、邱某、郑3某等人聚集后,行至乐某商务宾馆门前大街上时,与被害人高某乙、王某乙、王某甲、李某等人相遇,王4某、王某丁等人就从郑3某驾驶的面包车上拿出杀高某乙系创伤性、失血性休克死亡;王某甲所受之伤为轻伤一级;王某乙所受之伤为轻伤二级;李某所受之伤为轻微伤。公诉机关认为,被告人王某丁伙同王4某等人××他人身体,致一人死亡,二人受轻伤,一人受轻微伤,其行为触犯了《中华人民共和国刑法》××××,犯罪事实清楚,证据确实、充分,应当以××罪追究其刑事责任。王某丁在共同犯罪中其次起次要作用,是从犯,依法应当从轻、减轻或者免除处罚;其到案后如实公式供述自己的罪行,是坦白,依法可从轻处罚。根据《中华人民共和国刑事诉讼法》××的规定,特对被告人王某丁提起公诉。请依法判处。",
"opinion": "",
"judge": 42
}
本次比赛的数据样例,该数据用于微调阶段:
{
"caseCause": "走私普通货物、物品罪",
"justice": "经审理查明,2015年4月8日15时许,被告人刘某经拱北口岸旅检现场无申报通道进境,无书面向海关申报,被海关关员截查。关员从其携带的手提袋内查获“ ”牌酵素液2瓶、“”牌面霜1瓶、“”牌沐浴乳1瓶、“”牌洗碗精1瓶、“”牌果味软糖3瓶、“”牌花洒1个。被告人刘某自述其为赚取带工费人携带上列货物过关。经闸口海关核定,上述货物偷逃应缴税额共计人民币。 另查明,被告人刘某因走私分别于2014年8月17日、10月7日被闸口海关给予行政处罚。",
"opinion": "本院认为,被告人刘某一年内曾因走私被给予二次行政处罚后,又逃避海关监管,走私普通货物入境,其行为已构成走私普通货物罪。公诉机关指控的事实、罪名成立,应予支持。被告人刘某归案后如实供述自己的罪行,可从轻处罚。根据被告人刘某的犯罪情节和悔罪表现,适用缓刑确实不致再危害社会,可以宣告缓刑。依照《中华人民共和国刑法》第一百五十三条第一款第(一)项、第六十七条第三款、第七十二条第一款、第三款、第七十三条第一款、第三款、第五十二条、第六十四条的规定,",
"province": "广东省",
"judge": 1
}
给定样本的预测刑期
y
i
′
y_i^{\prime}
yi′和真实刑期
y
i
y_i
yi,当前样本的分数为:
score
i
=
{
1
,
∣
y
i
−
y
i
′
∣
⩽
0.25
y
i
0
,
∣
y
i
−
y
i
′
∣
>
0.25
y
i
\operatorname{score}_i=\left\{
即预测刑期和真实刑期的偏离程度≤25%视为正确,否则视为错误。测试集最终准确率为所有样本分数的均值。
所谓多任务预训练指的是在预训练阶段不仅仅是做了MLM任务,还利用往年数据做了一个有监督的分类任务,这种做法的动机是让预训练任务尽可能的贴近下游的微调任务,具体多任务损失函数设计如下:
L
o
s
s
总
=
l
o
s
s
m
l
m
+
l
o
s
s
c
l
s
Loss_总=loss_{mlm}+loss_{cls}
Loss总=lossmlm+losscls
考虑到文本序列较长,本文在预训练基模型选型上选用了Nezha模型。
代码:
class NezhaPreTraining(NeZhaPreTrainedModel): _keys_to_ignore_on_load_unexpected = [r"seq_relationship"] def __init__(self, config): super().__init__(config) self.bert = NeZhaModel(config) self.cls = BertOnlyMLMHead(config) # 增加分类模块 self.legal_cls = nn.Linear(config.hidden_size, config.num_labels) self.init_weights() def get_output_embeddings(self): return self.cls.predictions.decoder def set_output_embeddings(self, new_embeddings): self.cls.predictions.decoder = new_embeddings def forward( self, input_ids=None, attention_mask=None, token_type_ids=None, head_mask=None, position_ids=None, inputs_embeds=None, labels=None, next_sentence_label=None, ): outputs = self.bert( input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids, head_mask=head_mask, inputs_embeds=inputs_embeds, ) sequence_output, pooled_output = outputs[:2] prediction_scores = self.cls(sequence_output) seq_relationship_score = self.legal_cls(pooled_output) outputs = (prediction_scores, seq_relationship_score,) + outputs[2:] total_loss = None if labels is not None and next_sentence_label is not None: loss_fct = nn.CrossEntropyLoss() # MLM损失 masked_lm_loss = loss_fct(prediction_scores.view(-1, self.config.vocab_size), labels.view(-1)) # 分类损失 next_sentence_loss = loss_fct(seq_relationship_score.view(-1, self.config.num_labels), next_sentence_label.view(-1)) # 总损失 total_loss = masked_lm_loss + next_sentence_loss outputs = (total_loss,) + outputs return outputs
在比赛过程中,主要采用了两种模型结构实现微调,分别为Nezha+CNN、Nezha+LSTM和Nezha+Linear,下面直接上模型代码。
Nezha+CNN
class NeZhaCNN(NeZhaPreTrainedModel): def __init__(self, config): super().__init__(config) self.num_labels = config.num_labels self.bert = NeZhaModel(config) self.dropout = nn.Dropout(config.hidden_dropout_prob) self.filter_num = 256 self.filter_sizes = [3, 4, 5] self.convs = nn.ModuleList([nn.Conv2d(1, self.filter_num, (k, config.hidden_size)) for k in self.filter_sizes]) self.fc_cnn = nn.Linear(self.filter_num * len(self.filter_sizes), self.config.num_labels) self.classifier = nn.Linear(config.hidden_size, self.config.num_labels) self.init_weights() @add_start_docstrings_to_model_forward(BERT_INPUTS_DOCSTRING.format("batch_size, sequence_length")) def forward( self, input_ids=None, attention_mask=None, token_type_ids=None, position_ids=None, head_mask=None, inputs_embeds=None, labels=None, ): outputs = self.bert( input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids, head_mask=head_mask, inputs_embeds=inputs_embeds, ) last_hidden_state = outputs[0] x = last_hidden_state.unsqueeze(1) x = [F.relu(conv(x)).squeeze(3) for conv in self.convs] x = [F.max_pool1d(item, item.size(2)).squeeze(2) for item in x] x = torch.cat(x, 1) x = self.dropout(x) logits = self.fc_cnn(x) outputs = (logits,) + outputs[2:] if labels is not None: if self.num_labels == 1: loss_fct = MSELoss() loss = loss_fct(logits.view(-1), labels.view(-1)) else: loss_fct = CrossEntropyLoss() loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1)) outputs = (loss,) + outputs return outputs # (loss), logits, (hidden_states), (attentions)
Nezha+LSTM
class NeZhaLSTM(NeZhaPreTrainedModel): def __init__(self, config): super(NeZhaLSTM, self).__init__(config) self.num_labels = config.num_labels self.bert = NeZhaModel(config) self.dropout = nn.Dropout(config.hidden_dropout_prob) self.lstm = [] self.lstm_hidden_size = 512 self.lstm_layers = 1 self.lstm_dropout = 0.1 for i in range(self.lstm_layers): self.lstm.append(nn.LSTM(config.hidden_size if i == 0 else self.lstm_hidden_size * 4, self.lstm_hidden_size, num_layers=1, bidirectional=True, batch_first=True).cuda()) self.lstm = nn.ModuleList(self.lstm) self.classifier = nn.Linear(self.lstm_hidden_size * 2, self.config.num_labels) self.init_weights() @add_start_docstrings_to_model_forward(BERT_INPUTS_DOCSTRING.format("batch_size, sequence_length")) def forward( self, input_ids=None, attention_mask=None, token_type_ids=None, position_ids=None, head_mask=None, inputs_embeds=None, labels=None, ): outputs = self.bert( input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids, head_mask=head_mask, inputs_embeds=inputs_embeds, ) last_hidden_state = outputs[0] for lstm in self.lstm: try: lstm.flatten_parameters() except: pass output, (h_n, c_n) = lstm(last_hidden_state) x = h_n.permute(1, 0, 2).reshape(input_ids.size(0), -1).contiguous() x = self.dropout(x) logits = self.classifier(x) outputs = (logits,) + outputs[2:] # add hidden states and attention if they are here if labels is not None: if self.num_labels == 1: # We are doing regression loss_fct = MSELoss() loss = loss_fct(logits.view(-1), labels.view(-1)) else: loss_fct = CrossEntropyLoss() loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1)) outputs = (loss,) + outputs return outputs # (loss), logits, (hidden_states), (attentions)
Nezha+Linear
class NeZhaForSequenceClassification(NeZhaPreTrainedModel): def __init__(self, config): super().__init__(config) self.num_labels = config.num_labels self.bert = NeZhaModel(config) self.dropout = nn.Dropout(config.hidden_dropout_prob) self.classifier = nn.Linear(config.hidden_size, config.num_labels) self.init_weights() @add_start_docstrings_to_model_forward(BERT_INPUTS_DOCSTRING.format("batch_size, sequence_length")) def forward( self, input_ids=None, attention_mask=None, token_type_ids=None, position_ids=None, head_mask=None, inputs_embeds=None, labels=None, ): outputs = self.bert( input_ids, attention_mask=attention_mask, token_type_ids=token_type_ids, head_mask=head_mask, inputs_embeds=inputs_embeds, ) pooled_output = outputs[1] pooled_output = self.dropout(pooled_output) logits = self.classifier(pooled_output) outputs = (logits,) + outputs[2:] # add hidden states and attention if they are here if labels is not None: if self.num_labels == 1: # We are doing regression loss_fct = MSELoss() loss = loss_fct(logits.view(-1), labels.view(-1)) else: loss_fct = CrossEntropyLoss() loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1)) outputs = (loss,) + outputs return outputs # (loss), logits, (hidden_states), (attentions)
FGM对抗训练
模型融合
Focal Loss
…
阶段 | 性能 |
---|---|
复赛阶段 | 69.59 |
评审阶段 | 74.22 |
本文回顾了第四届“中国法研杯”司法人工智能挑战赛-刑期预测算法赛道比赛中的三等奖方案,使用多任务预训练和微调范式,并结合相关文本分类的上分trick优化nezha+CNN、nezha+LSTM和nezha+Linear三种结构模型,最终获得了不错的性能。
比赛链接:http://data.court.gov.cn/pages/laic2021.html
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。