★★★ 本文源自AI Studio社区精品项目,【点击此处】查看更多精品内容 >>>


基于飞桨的手语翻译与辅助教学系统

项目概述

世卫组织统计,现今有1.5亿人患有听力障碍,而到2030年这一数字将上升到2.5亿。手语是听障人群的主要沟通方式。但是在社交、医疗和工作等行业的手语服务需求却被长久忽视。我国手语行业发展缓慢并且手语教育普及度不够,手语教育行业人才紧缺成为目前亟需解决的社会问题。除此之外,手语自身存在地区差异,而标准手语推广不足。与口语相比,手语更关注话题,存在明显语序不同。两者之间需要逻辑转换,也即手语翻译。作为视觉语言,手语很难从简单文本描述中清晰学习。这些都给手语学习带来了困难。

当前市场上的手语服务平台只提供手语知识查询功能。手语教学多以长视频为主,不支持碎片化学习和查询学习。
为了改善听障人士的生活、工作和学习,本项目利用人工智能技术研发服务于听障人群和公共听力无障碍设施的手语辅助教学与翻译系统。系统旨在实现如下目标:

  • 基于自然语言处理和图像处理算法完成手语翻译功能
  • 基于手语翻译功能并结合手语纠错算法完成手语教学功能
  • 基于数据库技术构建手语知识库,丰富手语翻译和教学功能。

图1 手语翻译与辅助教学项目框架

图1展示了本项目主要框架,包括场景应用、服务功能算法核心三个层次。其中场景应用面向个人和公共领域开放系统服务接口,用户能够通过接口向系统输入口语或手语,并获取相应的系统反馈结果。在算法核心层,项目对经过数据处理后的口语或手语进行逻辑转换,转换结果将结合手语知识库中的信息送至服务功能层进行整理并展示。

项目平台前端展示如视频所示:

scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true">

项目技术方案

系统主要架构如图2所示,主要包括手语与口语双向翻译和手语辅助教学。手语与口语双向翻译包括口语到手语转换及手语到口语转换。口语转换手语以口语语音或文本为输入,通过自然语言处理模型将口语逻辑转换为对应手语逻辑,并将转换结果以文字和视频形式呈现。手语转换口语则执行相反的逻辑转换过程,系统接收手语动作视频为输入并输出口语文字或语音。其中手语动作视频中的手语提取由手语识别功能完成。系统除了提供手语与口语双向翻译结果,还会通过查询手语知识库,将与翻译结果关联的手语知识一并展现。相比于传统手语服务平台,本项目的主要创新点如下:

  • 针对现有手语翻译只能提供手语单词查询的问题,本系统实现连续手语翻译。输入连续口语,系统能够翻译出对应的手语句子,并以视频形式呈现。
  • 针对现有手语服务平台只提供手语到口语翻译服务的不足,本系统实现端到端的口语与手语双向翻译。通过嵌入手语识别模型,系统能够接受手语视频作为输入,并输出对应口语。
  • 通过构建手语知识库并集成手语纠错算法,系统实现手语辅助教学功能。

图2 手语翻译与辅助教学系统组成

系统主要功能均集成部署于云端,在移动端设备开放使用接口,用户能够轻易获取到本项目所提供各项的手语服务。

手语翻译

手语识别算法实现对连续输入的口语语音或文本与手语进行相互转换,主要在德语、英语场景下进行训练。

数据集介绍

我们使用PHOENIX-2014-T(德语,简称为ph14)和ASLG-PC12(英语,简称为aslg)两个数据集训练模型。数据集的语料统计分布情况如图3所示。

图3 手语翻译数据集介绍

模型框架

手语翻译模型的输入是自然文本,输出也是自然文本。图4直观地阐述手语翻译模型的执行流程,首先模型将输入文本分词为token(词元),然后再基于词向量预训练模型GLOVE对token进行向量化编码。在获得输入文本的编码后,采用Transformer架构实现Sequence-to-sequence模型,以自回归的方式生成翻译后的解码特征向量。最后基于softmax层解码对应的翻译句子的索引,拼接成最终的口语句子。

图4 手语翻译模型框架

主要挑战

这项工作的目标是将已识别的多个独立的手语序列(模型输入)转换为一个完整的口语句子(模型输出)。与一般的神经机器翻译相比,手语翻译主要面临着如下挑战:

  • 手语与口语表征空间的差异:手语的表示空间会明显小于口语句子,从而增加了网络学习的难度。

解决表征空间差异–多层级数据增强策略

除了现有数据集中的手语-口语对,我们还使用上采样方法作为我们的数据增强算法,并生成口语-口语对作为扩展样本,将口语信息引入到手语中。因而,扩大了手语的分布空间。从3个层级对采样规模进行约束:

  • 单词级别:单词差异率(Vocabulary Different Ratio,VDR),评估口语与手语单词级别上的差异。

以及稀疏词比例(Rare Vocabulary Ratio,RVR):

  • 句子级别:单词覆盖率(Sentence Cover Ratio,SCR)来评价句子对之间的相似度。
  • 数据集级别:采用数据集长度差异指标(Dataset Length-difference Ratio,DLR)来判断数据集层级上口语与手语的表示空间差异。

最后基于加权求和策略求得上采样的规模:

主要代码实现

定义模型初始化和数据增强函数:

import os
from tqdm import tqdm
import numpy as npdef data_preprocess(root_path):"""数据预处理,统计数据集的单词数、以及文本分词"""special_token=['<s>','<e>','<unk>']de_vocab=[]for file in tqdm(os.listdir(root_path)):file_path = os.path.join(root_path, file)with open(file_path, 'r') as f:for sentence in f.readlines():de_vocab.extend(sentence.strip().split(" "))de_vocab = list(set(de_vocab))print("vocab num: ", len(de_vocab))with open(os.path.join(root_path, "vocab.de"),'a+') as f:for item in special_token:f.write(item+'\n')for item in de_vocab:f.write(item+'\n')def data_augmentation(gloss_file, text_file, sampling_ratio):"""手语翻译 多层级数据增强算法"""gloss_data = []# 读所有手语样本=》gloss_datawith open(gloss_file, 'r') as f:gloss_sentence = [item.strip().lower() for item in f.read().split('\n')]for item in gloss_sentence:words = item.split()gloss_eff = []for word in words:if "__" not in word:gloss_eff.append(word)gloss_data.append(" ".join(gloss_eff))# 读所有口语句子=》text_datawith open(text_file, 'r', encoding='utf-8') as f:text_data = [item.strip() for item in f.read().split('\n')]gloss_data.pop(-1)# 设置采样ratioratio = int(sampling_ratio * len(gloss_data))index = np.random.randint(low=0, high=len(gloss_data)-1, size=ratio)# 生成口语-口语对进行上采样for item in index:gloss_data.append(text_data[item][0:-2])text_data.append(text_data[item])def model_definition(args):"""训练准备:定义模型、损失函数、优化器等参数"""# 定义transformer模型transformer = TransformerModel(src_vocab_size=args.src_vocab_size,trg_vocab_size=args.trg_vocab_size,max_length=args.max_length + 1,n_layer=args.n_layer,n_head=args.n_head,d_model=args.d_model,d_inner_hid=args.d_inner_hid,dropout=args.dropout,weight_sharing=args.weight_sharing,bos_id=args.bos_idx,eos_id=args.eos_idx)# 定义损失函数,这里输出为序列id的预测,所以采用交叉熵函数criterion = CrossEntropyCriterion(args.label_smooth_eps, args.bos_idx)# 定义学习率衰减策略scheduler = paddle.optimizer.lr.NoamDecay(args.d_model, args.warmup_steps, args.learning_rate, last_epoch=0)# 定义优化器optimizer = paddle.optimizer.Adam(learning_rate=scheduler,beta1=args.beta1,beta2=args.beta2,epsilon=float(args.eps),parameters=transformer.parameters())return transformer, criterion, scheduler, optimizer

训练过程如图5所示。

图5 手语翻译在飞桨平台的训练过程

运行如下代码加载模型参数并进行推理:

import paddle
import yaml
import time
from paddlenlp.data import Vocab, Pad
from paddlenlp.transformers import InferTransformerModel
import sys
sys.path.append('/home/aistudio/external-libraries')
from attrdict import AttrDictdef prepare_infer(args):# 加载vocab类vocab = Vocab.load_vocabulary(args.trg_vocab_fpath, bos_token=args.special_token[0],eos_token=args.special_token[1],unk_token=args.special_token[2])padding_vocab = (lambda x: (x + args.pad_factor - 1) // args.pad_factor * args.pad_factor)args.src_vocab_size = padding_vocab(len(vocab))args.trg_vocab_size = padding_vocab(len(vocab))# 定义模型transformer = InferTransformerModel(src_vocab_size=args.src_vocab_size,trg_vocab_size=args.trg_vocab_size,max_length=args.max_length + 1,num_encoder_layers=args.n_layer,num_decoder_layers=args.n_layer,n_layer=args.n_layer,n_head=args.n_head,d_model=args.d_model,d_inner_hid=args.d_inner_hid,dropout=args.dropout,weight_sharing=args.weight_sharing,bos_id=args.bos_idx,eos_id=args.eos_idx,beam_size=args.beam_size,max_out_len=args.max_out_len)# 加载模型参数model_dict = paddle.load(args.inference_model_dir)transformer.load_dict(model_dict)transformer.eval()return transformer, vocabdef post_process_seq(seq, bos_idx, eos_idx, output_bos=False, output_eos=False):"""后处理,从句号处截断"""eos_pos = len(seq) - 1for i, idx in enumerate(seq):if idx == eos_idx:eos_pos = ibreakseq = [idx for idx in seq[:eos_pos + 1]if (output_bos or idx != bos_idx) and (output_eos or idx != eos_idx)]return seqdef infer_spoken_sen(infer_input, infer_model, infer_vocab):"""infer入口"""infer_start = time.time()infer_input = infer_input.lower().split()tokens = infer_vocab.to_indices(infer_input)word_pad = Pad(args.bos_idx)tokens = word_pad([tokens+ [args.eos_idx]])id_lists = infer_model(src_word=paddle.to_tensor(tokens)).transpose([0, 2, 1]).numpy()seq = post_process_seq(id_lists[0][0], args.bos_idx, args.eos_idx)word_list = infer_vocab.to_tokens(seq)return " ".join(word_list), time.time()-infer_startif __name__ == '__main__':# 定义infer基本参数yaml_file = 'model_param/infer.yaml'with open(yaml_file, 'rt') as f:args = AttrDict(yaml.safe_load(f))random_seed = eval(str(args.random_seed))if random_seed is not None:paddle.seed(random_seed)start_time = time.time()model, vocab = prepare_infer(args)index_num = 28print("-"*index_num, " 手语翻译 测试开始 ", "-"*index_num)predict_sen = "region koennen nebel"result_sen, infer_time = infer_spoken_sen(predict_sen, model, vocab)end_time = time.time()print(" · 您输入的句子为(手语): ", predict_sen)print(" · 模型翻译结果(口语): ", result_sen)print(" · 本次翻译用时: \t{:.3f}s.".format(infer_time))print(" · 测试总用时(+模型加载): \t{:.3f}s.".format(end_time-start_time))print("-"*index_num, " 手语翻译 测试完成 ", "-"*index_num)
----------------------------  手语翻译 测试开始  ----------------------------· 您输入的句子为(手语):  region koennen nebel· 模型翻译结果(口语):  in den niederungen bildet sich hier und da nebel .· 本次翻译用时:  0.801s.· 测试总用时(+模型加载):  2.227s.
----------------------------  手语翻译 测试完成  ----------------------------

手语识别

手语识别算法的主要功能是识别出手语视频中对应的手语动作,并按照其出现的顺序排列。项目采用容易获取手语动作信息的RGB摄像头作为输入设备。手语动作识别处理流程如图6所示,摄像头作为输入设备采集用户手语动作视频,将其转换为图片序列作为手语识别算法模型输入。模型采用基于注意力机制的编码器架构作为主体,其本质是将图片序列转换为手语文本序列的Sequence-to-Sequence模型。模型首先通过卷积网络提取图片特征并通过周期函数引入其位置和时序信息,然后由编码器学习图片序列中的手语信息和上下文语义关联特征。最后模型通过简单的线性层和softmax层激活得到手语单词。

图6 手语识别模型框架

主要代码实现

定义图片卷积层:

class SpatialEmbeddings(nn.Layer):"""图片卷积层"""def __init__(self,embedding_dim: int,input_size: int,):"""网络初始化参数:embedding_dim: 图片特征隐层维度input_size: 图片特征嵌入维度"""super().__init__()self.embedding_dim = embedding_dimself.input_size = input_sizeself.ln = nn.Linear(self.input_size, self.embedding_dim)self.norm = MaskedNorm(num_features=embedding_dim)self.activation = nn.Softsign()def forward(self, x: Tensor, mask: Tensor) -> Tensor:"""前向传播参数:mask: 图片特征maskx: 输入图片特征返回:图片深度特征"""x = self.ln(x)x = self.norm(x, mask)x = self.activation(x)return xclass MaskedNorm(nn.Layer):"""对有mask的输入进行批归一化参考https://discuss.pytorch.org/t/batchnorm-for-different-sized-samples-in-batch/44251/8"""def __init__(self,num_features):super().__init__()self.norm = nn.BatchNorm1D(num_features=num_features)self.num_features = num_featuresdef forward(self, x: Tensor, mask: Tensor):if self.training:reshaped = x.reshape([-1, self.num_features])reshaped_mask = mask.reshape([-1, 1]) > 0selected = paddle.masked_select(reshaped, reshaped_mask.tile([1,reshaped.shape[1]])).reshape([-1, self.num_features])batch_normed = self.norm(selected)scattered = reshapedscattered[reshaped_mask.reshape([-1])] = batch_normedreturn scattered.reshape([x.shape[0], -1, self.num_features])else:reshaped = x.reshape([-1, self.num_features])batched_normed = self.norm(reshaped)return batched_normed.reshape([x.shape[0], -1, self.num_features])

定义基于注意力的编码器层:

class TransformerEncoder(Encoder):"""基于注意力机制的编码器层"""def __init__(self,hidden_size: int = 512,ff_size: int = 2048,num_layers: int = 8,num_heads: int = 4,dropout: float = 0.1,emb_dropout: float = 0.1,):"""参数初始化参数:hidden_size: 隐层嵌入维度ff_size: 前向传播层维度num_layers: 基于注意力的卷积层数num_heads: 多头注意力头数dropout: dropout概率emb_dropout: 嵌入层dropout概率."""super(TransformerEncoder, self).__init__()# 构建注意力卷积层self.layers = nn.LayerList([TransformerEncoderLayer(size=hidden_size,ff_size=ff_size,num_heads=num_heads,dropout=dropout,)for _ in range(num_layers)])self.layer_norm = nn.LayerNorm(hidden_size, epsilon=1e-6)self.pe = PositionalEncoding(hidden_size) # 位置嵌入self.emb_dropout = nn.Dropout(p=emb_dropout)self._output_size = hidden_sizedef forward(self, embed_src: Tensor, src_length: Tensor, mask: Tensor) -> (Tensor, Tensor):"""前向传播参数:embed_src: 图片特征输入src_length: 输入长度mask: 指明mask区域返回:隐层状态"""x = self.pe(embed_src)x = self.emb_dropout(x)for layer in self.layers:x = layer(x, mask)return self.layer_norm(x), Noneclass PositionalEncoding(nn.Layer):"""位置嵌入编码"""def __init__(self, size: int = 0, max_len: int = 5000):"""初始化位置编码参数:max_len: 最大长度dropout: dropout概率"""if size % 2 != 0:raise ValueError("Cannot use sin/cos positional encoding with ""odd dim (got dim={:d})".format(size))pe = paddle.zeros([max_len, size])position = paddle.arange(0, max_len).unsqueeze(1)div_term = paddle.exp((paddle.arange(0, size, 2, dtype=paddle.float32) * -(math.log(10000.0) / size)))pe[:, 0::2] = paddle.sin(paddle.cast(position, 'float32') * div_term)pe[:, 1::2] = paddle.cos(paddle.cast(position, 'float32') * div_term)pe = pe.unsqueeze(0)  # shape: [1, size, max_len]super(PositionalEncoding, self).__init__()self.register_buffer("pe", pe)self.dim = sizedef forward(self, emb):"""嵌入位置编码到输入中参数:输入图片特征返回:嵌入位置信息的图片特征"""return emb + self.pe[:, : emb.shape[1]]class TransformerEncoderLayer(nn.Layer):"""基于注意力机制的卷积层"""def __init__(self, size: int = 0, ff_size: int = 0, num_heads: int = 0, dropout: float = 0.1):"""卷积层初始化参数:ff_size: 隐层表示向量维度num_heads: 注意力头数dropout: dropout概率"""super(TransformerEncoderLayer, self).__init__()self.layer_norm = nn.LayerNorm(size, epsilon=1e-6)self.src_src_att = MultiHeadedAttention(num_heads, size, dropout=dropout)self.feed_forward = PositionwiseFeedForward(input_size=size, ff_size=ff_size, dropout=dropout)self.dropout = nn.Dropout(dropout)self.size = sizedef forward(self, x: Tensor, mask: Tensor) -> Tensor:"""前向传播, 之后进行自注意力特征提取, 然后使用dropout避免过拟合参数: x: 输入mask: 输入掩码返回:隐层特征"""x_norm = self.layer_norm(x) # 层归一化h = self.src_src_att(x_norm, x_norm, x_norm, mask) # 自注意力h = self.dropout(h) + x # 残差连接o = self.feed_forward(h)return oclass MultiHeadedAttention(nn.Layer):"""多头注意力,参考论文"Attention is All You Need""""def __init__(self, num_heads: int, size: int, dropout: float = 0.1):super(MultiHeadedAttention, self).__init__()assert size % num_heads == 0self.head_size = head_size = size // num_headsself.model_size = sizeself.num_heads = num_headsself.k_layer = nn.Linear(size, num_heads * head_size)self.v_layer = nn.Linear(size, num_heads * head_size)self.q_layer = nn.Linear(size, num_heads * head_size)self.output_layer = nn.Linear(size, size)self.softmax = nn.Softmax(axis=-1)self.dropout = nn.Dropout(dropout)def forward(self, k: Tensor, v: Tensor, q: Tensor, mask: Tensor = None):batch_size = k.shape[0]num_heads = self.num_headsk = self.k_layer(k)v = self.v_layer(v)q = self.q_layer(q)k = paddle.transpose(k.reshape([batch_size, -1, num_heads, self.head_size]), [0, 2, 1, 3])v = paddle.transpose(v.reshape([batch_size, -1, num_heads, self.head_size]), [0, 2, 1, 3])q = paddle.transpose(q.reshape([batch_size, -1, num_heads, self.head_size]), [0, 2, 1, 3])q = q / math.sqrt(self.head_size)scores = paddle.matmul(q, k.transpose([0, 1, 3, 2]))if mask is not None:scores = masked_fill(scores, ~mask.unsqueeze(1), float("-inf"))attention = self.softmax(scores)attention = self.dropout(attention)context = paddle.matmul(attention, v)context = context.transpose([0, 2, 1, 3]).reshape([batch_size, -1, num_heads * self.head_size])output = self.output_layer(context)return outputclass PositionwiseFeedForward(nn.Layer):"""多层感知机"""def __init__(self, input_size, ff_size, dropout=0.1):"""参数:input_size: 输入维度ff_size: 隐层维度dropout: dropout概率"""super(PositionwiseFeedForward, self).__init__()self.layer_norm = nn.LayerNorm(input_size, epsilon=1e-6)self.pwff_layer = nn.Sequential(nn.Linear(input_size, ff_size),nn.ReLU(),nn.Dropout(dropout),nn.Linear(ff_size, input_size),nn.Dropout(dropout))def forward(self, x):x_norm = self.layer_norm(x)return self.pwff_layer(x_norm) + x

模型定义:

class SignModel(nn.Layer):"""模型基类"""def __init__(self,encoder: Encoder,gloss_output_layer: nn.Layer,sgn_embed: SpatialEmbeddings,gls_vocab: GlossVocabulary):"""模型初始化参数encoder: 编码器层gloss_output_layer: 输出层sgn_embed: 图片卷积层gls_vocab: 手语词典"""super().__init__()self.encoder = encoderself.sgn_embed = sgn_embedself.gls_vocab = gls_vocabself.gloss_output_layer = gloss_output_layerdef forward(self,sgn: Tensor,sgn_mask: Tensor,sgn_lengths: Tensor) -> (Tensor, Tensor, Tensor, Tensor):"""模型前向传播参数:sgn: 手语输入sgn_mask: 输入掩码sgn_lengths: 输入长度返回:模型预测结果"""encoder_output, encoder_hidden = self.encode(sgn=sgn, sgn_mask=sgn_mask, sgn_length=sgn_lengths)gloss_scores = self.gloss_output_layer(encoder_output)gloss_probabilities = nn.functional.log_softmax(gloss_scores,axis=2) # 激活层gloss_probabilities = gloss_probabilities.transpose([1, 0, 2])return gloss_probabilitiesdef encode(self, sgn: Tensor, sgn_mask: Tensor, sgn_length: Tensor) -> (Tensor, Tensor):return self.encoder(embed_src=self.sgn_embed(x=sgn, mask=sgn_mask),src_length=sgn_length,mask=sgn_mask)def get_loss_for_batch(self,batch: Batch,recognition_loss_function: nn.Layer) -> Tensor:"""损失计算参数:batch: 输入数据recognition_loss_function: 手语识别损失函数, 默认为CTC损失recognition_loss_weight: 损失权重返回:recognition_loss: 总损失大小"""gloss_probabilities = self.forward(sgn=batch.sgn,sgn_mask=batch.sgn_mask,sgn_lengths=batch.sgn_lengths)assert gloss_probabilities is not Nonerecognition_loss = (recognition_loss_function(gloss_probabilities,batch.gls.cast('int32'),batch.sgn_lengths.cast('int64'),batch.gls_lengths.cast('int64')))return recognition_loss

构建手语识别模型:

def build_model(sgn_dim: int,gls_vocab: GlossVocabulary
) -> SignModel:"""构建手语识别模型参数:sgn_dim: 手语图片嵌入特征维度gls_vocab: sign gloss vocabulary返回:初始化完成的模型"""sgn_embed: SpatialEmbeddings = SpatialEmbeddings(embedding_dim = 512,input_size=sgn_dim)encoder = TransformerEncoder(hidden_size = 512,ff_size=2048,num_layers = 3,num_heads = 8,dropout = 0.1,emb_dropout=0.1)gloss_output_layer = nn.Linear(encoder.output_size, len(gls_vocab))model: SignModel = SignModel(encoder=encoder,gloss_output_layer=gloss_output_layer,sgn_embed=sgn_embed,gls_vocab=gls_vocab)return model

训练损失函数如图7所示,更多训练和测试数据见项目VisualDL可视化日志。

图7 手语识别模型训练损失

手语教学

本系统第二个核心功能是基于手语翻译功能进行扩展,实现手语教学功能。手语教学旨在帮助听障用户完成手语的系统性巩固学习。如图8所示,教学模式分为以下两种:

  • 检索模式:用户输入想要学习的语句或单词,系统除了呈现手语翻译结果,还将在知识库中进行检索,给出关联知识,其中包括手语逻辑、单词知识、规范用例等,借此实现视频演示和知识详解结合的教学功能。
  • 练习模式:基于对用户手语学习历史的分析,给定口语句子作为题目。用户需要根据提示完成手语动作并上传视频至系统。系统将识别用户手语动作并转换为手语序列。如果出现手语错误或遗漏,则给出即时反馈用于纠正。

图8 手语教学中的练习模式与检索模式示例

手语练习模式基于手语纠错算法进行开发,图9展示了手语纠错算法示例,该算法基于字符串匹配实现,算法对手语识别结果和正确答案进行比对,将错词、漏词和语序逻辑等错误情况汇总并给出评分。

图9 手语纠错流程例

主要代码实现

下面代码展示了手语纠错算法示例:

# 手语教学,根据用户输入的视频识别出用户输入的手语动作序列,并进行打分
import time
import numpy as npclass InputChecker:def __init__(self):self.score = 0.0self.wrong_type = ["顺序错误", "词语缺失", "多余输入"]self.wrong_type_max = [30, 50, 20]def evaluate_user_input(self, gold_ans, input_text):"""对每一个用户输入评估三种类型的得分情况"""gold_ans = gold_ans.split(" ")input_text = input_text.split(" ")self.score = self.cover_tokens(gold_ans, input_text) # 初始化得分if self.score == 0:return self.score, "很遗憾,请再重新学习一下吧。"order_score, order_wrong_param = self.order_check(gold_ans, input_text) # 顺序错误token_score, token_miss_param = self.token_check(gold_ans, input_text) # 词语缺失extra_score, extra_input_param = self.extra_input_check(gold_ans, input_text) # 多余输入self.score -= (order_score + token_score + extra_score)info = {self.wrong_type[0]: order_wrong_param, self.wrong_type[1]: token_miss_param, self.wrong_type[2]: extra_input_param}return self.score, infodef cover_tokens(self, gold_ans, input_text):cover_num = sum([1 for item in gold_ans if item in input_text])if cover_num == 0:return 0.0else:return 100.0def order_check(self, gold_ans, input_text):"""顺序错误检查"""common_list = [item for item in gold_ans if item in input_text]if len(common_list) < 2:return 0, Noneelse:order_shuffle_score = 0wrong_order_pairs = []for i in range(len(common_list)):for j in range(i+1, len(common_list)):pos_gold = [gold_ans.index(common_list[i]), gold_ans.index(common_list[j])]pos_ans = [input_text.index(common_list[i]), input_text.index(common_list[j])]if (pos_gold[1] > pos_gold[0] and pos_ans[1] > pos_ans[0]) or (pos_gold[1] < pos_gold[0] and pos_ans[1] < pos_ans[0]):continueelse:wrong_order_pairs.append(common_list[i] + "_" + common_list[j])order_shuffle_score += 10if len(wrong_order_pairs) > 0:return min(order_shuffle_score, self.wrong_type_max[0]), " | ".join(wrong_order_pairs)else:return 0, Nonedef token_check(self, gold_ans, input_text):"""词语缺失检查"""miss_num = sum([1 for item in gold_ans if item not in input_text])miss_tokens = [item for item in gold_ans if item not in input_text]minus_score = 10 * miss_numif len(miss_tokens) > 0:return min(minus_score, self.wrong_type_max[1]), " | ".join(miss_tokens)else:return 0, Nonedef extra_input_check(self, gold_ans, input_text):"""多余输入检查"""extra_num = sum([1 for item in input_text if item not in gold_ans])extra_tokens = [item for item in input_text if item not in gold_ans]minus_score = 10 * extra_numif len(extra_tokens) > 0:return min(minus_score, self.wrong_type_max[2]), " | ".join(extra_tokens)else:return 0, Noneif __name__ == '__main__':# 定义infer基本参数user_input = ["凉爽 骑车", "天气 我 出门 不", "热 天气 我 不 出门", "温度 热 我 骑车", "天气 热 我 出门 不"]golden = "天气 热 我 出门 不"start_time = time.time()index_num = 23input_checker = InputChecker()print("-"*index_num, " 手语教学 测试开始 ", "-"*index_num)for i in range(len(user_input)):score, info = input_checker.evaluate_user_input(golden, user_input[i])print(" => 学习记录 {} :".format(str(i)))print(" · 用户输入: ", " / ".join(user_input[i].split(" ")))print(" · 正确答案: ", " / ".join(golden.split(" ")))print(" · 综合评分:  {:.1f}".format(score))if isinstance(info, str):print(" . ", info)else:wrong_type = [key for key, value in info.items() if value is not None]wrong_type_param = [value for key, value in info.items() if value is not None]if len(wrong_type) > 0:print(" · 错误类型: ", "|".join(wrong_type))print(" · 详细信息: ")for i, j in zip(wrong_type, wrong_type_param):print(" \t\t {} : {}".format(i, j))else:print(" · 恭喜,完全正确!本关闯关成功!")print(" · 本次服务用时:\t{:.3f}s.".format(time.time()-start_time))print("-"*index_num, " 手语教学 测试完成 ", "-"*index_num)
-----------------------  手语教学 测试开始  -----------------------=> 学习记录 0 :· 用户输入:  凉爽 / 骑车· 正确答案:  天气 / 热 / 我 / 出门 / 不· 综合评分:  0.0.  很遗憾,请再重新学习一下吧。=> 学习记录 1 :· 用户输入:  天气 / 我 / 出门 / 不· 正确答案:  天气 / 热 / 我 / 出门 / 不· 综合评分:  90.0· 错误类型:  词语缺失· 详细信息: 词语缺失 : 热=> 学习记录 2 :· 用户输入:  热 / 天气 / 我 / 不 / 出门· 正确答案:  天气 / 热 / 我 / 出门 / 不· 综合评分:  80.0· 错误类型:  顺序错误· 详细信息: 顺序错误 : 天气_热 | 出门_不=> 学习记录 3 :· 用户输入:  温度 / 热 / 我 / 骑车· 正确答案:  天气 / 热 / 我 / 出门 / 不· 综合评分:  50.0· 错误类型:  词语缺失|多余输入· 详细信息: 词语缺失 : 天气 | 出门 | 不多余输入 : 温度 | 骑车=> 学习记录 4 :· 用户输入:  天气 / 热 / 我 / 出门 / 不· 正确答案:  天气 / 热 / 我 / 出门 / 不· 综合评分:  100.0· 恭喜,完全正确!本关闯关成功!· 本次服务用时:   0.007s.
-----------------------  手语教学 测试完成  -----------------------

[C4-AI2022]基于飞桨的手语翻译与辅助教学系统相关推荐

  1. [C4AI_2022]基于飞桨的无人机智能工地安全监管系统

    ★★★ 本文源自AI Studio社区精品项目,[点击此处]查看更多精品内容 >>> 基于飞桨的无人机智能工地安全监管系统 项目展示 scrolling="no" ...

  2. 基于“飞桨”的深度学习智能车

    本文作者:吴东昱,北京钢铁侠科技深度学习算法工程师 前言   我在观察历届智能车竞赛以及教学实验中发现,采用传统视觉算法的视觉智能车只能在特定赛道中行驶,一旦赛道环境改变,必须修改大量的代码才能运行. ...

  3. 搭建基于飞桨的OCR工具库,总模型仅8.6M的超轻量级中文OCR,单模型支持中英文数字组合识别、竖排文本识别、长文本识别的PaddleOCR

    介绍 基于飞桨的OCR工具库,包含总模型仅8.6M的超轻量级中文OCR,单模型支持中英文数字组合识别.竖排文本识别.长文本识别.同时支持多种文本检测.文本识别的训练算法. 相关链接 PaddleOCR ...

  4. 基于飞桨 DeepLabV3+实现人体肾组织图像中肾小球识别

    基于飞桨 DeepLabV3+实现人体肾组织图像中肾小球识别 一.项目背景 估计显示,地球上有超过70亿人,银河系中有3000亿颗恒星.相比之下,成年人体内含有37万亿个细胞.确定这些细胞之间的功能和 ...

  5. 赛桨PaddleScience v1.0 Beta:基于飞桨核心框架的科学计算通用求解器

    近年来,关于AI for Science的主题被广泛讨论,重点领域包含使用AI方法加速设计并发现新材料,助力高能物理及天文领域的新问题探索,以及加速智慧工业实时设备数据与模型的"数字孪生&q ...

  6. 基于飞桨实现高精度岩相自动分析,助力油气田勘探开发设计

    1. 概述 1.1 行业背景与痛点 岩相分析是以岩石薄片的微观描述和分类为基础的研究工作,也是沉积和成岩研究的一项重要技术,对于油气勘探开发的工程实践具有基础性指导地位.通过薄片分析矿物的比例.分布. ...

  7. 基于飞桨图像分类套件PaddleClas的柠檬分类竞赛实战

    前情提要   通过之前教程中的学习,相信大家对于如何搭建一个分类网络已经清晰了.那么我们不禁会想,有没有更快速的尝试模型及技巧的方法呢?因为我们在上一次课程中使用的代码都需要自己进行开发,自己写需要很 ...

  8. 基于飞桨的智能课堂行为分析与考试作弊检测系统

    智慧课堂:基于飞桨的智能化课堂 本项目主要实现了课堂专注度分析与考试作弊检测两个功能,通过对学生的姿态检测,可以有效的辅助老师有效监督学生的学习上课情况,对学生的上课行为进行分析及评分,避免出现课堂不 ...

  9. 基于飞桨复现图像分类模型TNT,实现肺炎CT分类

    本项目介绍了TNT图像分类模型,讲述了如何使用飞桨一步步构建TNT模型网络结构,并尝试在新冠肺炎CT数据集上进行分类.由于作者水平有限,若有不当之处欢迎批评指正. TNT模型介绍 TNT模型全称是Tr ...

最新文章

  1. etcd、flannel的安装---单节点
  2. python 3d绘图 拖动_使用python-matplotlib连续3D绘图(即图形更新)?
  3. 完整的node脚手架搭建服务
  4. 实现滚到div时淡入效果
  5. css 关闭按钮实现,CSS做的关闭按钮动效
  6. 任务调度之Timer、TimerTask
  7. 像目标主机一样的tcp流重组
  8. 【布局优化】基于人工鱼群算法实现充电桩布局优化matlab源码
  9. KOOCAN的影视资讯——那些惊艳到你的女鬼
  10. PHP开发APP接口实现--基本篇
  11. Docker下载安装
  12. conda安装本地whl文件
  13. 多元线性回归—异方差
  14. AcWing 1123 铲雪车
  15. 计算机专业的你,毕业后可以从事什么职业?前景如何?
  16. 工作无聊?程序员上班没事做该怎么办!
  17. Office的Ctrl C,Ctrl V使用过多?一起看看python如何处理
  18. IT项目经理在面试时如何巧妙的回答老板提出的问题
  19. 集体智慧编程学习之核方法
  20. 03pe修改计算机名称,[U盘PE教程]玩转PE内置注册表(基于NT6.0)

热门文章

  1. 企业会计信息化风险控制体系探讨
  2. MATLAB矩阵的加法和减法、MATLAB除法、标量操作
  3. 如何切确成立TYPE、partition(分区)?
  4. windows10系统下PS、AI等软件界面字很小如何解决
  5. 移动web开发清除默认样式设置
  6. 面向GPU的多LOD因子的大规模场景可视化策略 (转)
  7. php视频设置背景音乐,视频怎么添加背景音乐 给视频添加背景音乐
  8. 偷偷爆料下各公司年终奖情况!(1.30 日最新版)
  9. 无需软件windows如何加密文件夹
  10. 是工人养活了资本家还是资本家养活了工人?