- 云函数中实现耗时操作解决方案
起因 在实际开发业务中需要生成带图的表格,由于数据过多导致服务超时。当时我通过在 腾讯云控制台 设置的时间的函数超时600秒,没到时间就超时了。 异常信息如下: WAServiceMainContext.js:2 Error: cloud.callFunction:fail Error: errCode: -501002 resource server timeout | errMsg: ESOCKETTIMEDOUT 后来通过和官方人员沟通得知小程序基础库的 callFunction 接口的默认限制了云函数超时时间的设置为60秒的上限,无法通过腾讯云控制台修改突破限制。 解决方案 如果不是通过callFunction调用的云函数是可以突破限制的,最多可以设置900秒,所以我用了两个云函数来解决这个超时问题。 云函数A用于小程序调用。 云函数B执行耗时操作,设置超时时间为900秒。 小程序调用云函数A,云函数A不用await修饰符调用云函数B,(云函数内互相调用是稳定的)然后云函数A返回调用成功,小程序这边收到云函数A的返回值就知道任务正在执行了,在小程序A里面去数据库存储一条开始执行状态的数据,返回ID。 然后在云函数B执行耗时操作完成去修改数据库的数据状态。 最后在小程序端监听数据库具体ID数据的状态变化来对用户进行反馈。 总结 当然如果数据量超大的话 900秒也会被用完,优化代码是一方面,但是如果代码优化不了的情况下这个时候就需要与产品功能想一个更好的解决方案。 假如900秒最多导出5000条数据,那么超过5000条就可以让用户分页导出,这样的话又可以保证不超时又能满足用户的方案。
2022-09-21 - 根据大佬们的分享,简单提供一些有用的东西分享,只对新手有用。
1.模型结构,基本都采用的郭大分享的模型结构,简要概述一下是怎样的:文本有 title、asr、ocr 三种,通过将三种文本拼接在一起,其中拼接方式有很多种(a、郭大所分享的是对 title、 asr、ocr分别均匀截断,然后拼接,如 [CLS] + title + [SEP] + asr + [SEP] + ocr + [SEP] 这种形式, 我的实现方式为:加载BertTokenizer,分别对 title、 asr、 ocr 进行分词,然后写个 if,如果 title 、 asr 、ocr 分词后的长度超过了单个字段最大长,这里假定为 84,因为(256 – 4)/3 = 84,这里 4 代表 4 个符号 1 个 [CLS] 和 3 个 [SEP],然后如果单个字段大于 84 的话,就截断,如果没有的话就不需要管,if 判断之后按照上面的形式 拼接在一起,然后通过 Berttokenizer 的 convert tokens to ids 进行转换,得到对应的 ids,(attention_mask 的生成,就是 [1] * len(ids) 就可以了)最后再进行 padding 的操作,即 补 0 。),也有同学说过直接拼接,不进行截断的方式会得到更高的分数,这里的原因个人认为可能是因为 title 和 asr 字段是包含了短视频全局的信息,而截断的方式可能会造成信息的损失(针对这个问题,挥霍大佬提出的使用tfidf特征来补足那些已截断句子的信息缺失,个人实现过把此类特征加入到模型内部进行融合,但是没有取得提升,一可能是自己的实现有问题,二可能是 tfidf 的特征向量空间与 bert 的不符,但是本人通过将 tdidf 也经过 bert 的 embedding 层进行映射,然后融入到 bert 中一同进行编码,但还是未取得提升),并且,ocr 字段中、相对于 title 和 asr 字段会存在更多的噪声,而噪声问题会对模型的精度产生很大程度的影响。现在开始介绍模型结构方面,大家应该都仔细看过郭大分享的模型结构,对于文本的特征,就是通过 bert 的 embedding 层得到 (shape 为 [bs, seq_length, hidden_size]),然后 video 的特征,首先经过一层线性层进行 dense ( shape 为 [bs, frame size, hidden_size]),然后再经过一个激活函数(这里可以理解为 video 的特征是通过 swin transformer 进行提取的,特征的分布和 bert 的极为不一致的,通过线性层的映射,可以稍微的缓解异质空间的不一致问题,后来,一群的陪跑大佬分享了和 QQ 浏览器第一名开源方案的做法在郭大分享的模型结构上的改进,即将通过线性层映射之后的 video 特征再经过与文本特征经过的同一个的 bert embedding 层( shape 为 [bs, frame size, hidden_size]),使得 video embedding 经过 bert 的 embedding 层进行映射,让 video embedding 可以或者 和 text embedding一样的特征分布,实验结果证明,这样做确实在郭大分享的模型结构的基础上,进一步的提升。)、然后通过将两个 embedding 进行拼接( shape 为 [bs, seq_length + frame size, hidden_size]),然后再将两个 embedding 对应的 mask 也进行拼接,对于拼接之后的 mask 进行一个BertModel 函数内部的 self.get_extended_attention_mask 这个方法所做的生成 extended_attention_mask ,为模型计算注意力的时候忽略那些 padding 的地方。最后,将拼接的 embedding 与 mask 送入到 bert 的 encoder 中去编码,得到最后一层的隐层状态 last hidden states( shape 为 [bs, seq_length + frame size, hidden_size]),然后通过 mean pooling 操作,得到最后的特征表示,最后经过线性层进行 200 分类。以上就是模型结构的大致介绍,本人试着去实现过一些 NLP 的基本操作,如将 encoder 输出的后 4 层进行拼接取 mean 什么的,还有通过 multi sample dropout 来提升模型的泛化能力,但在这次比赛中并未取得提升,所以目前最有效的还是如上面所述的最简单的操作。如果想要做到比别人好,可以自己设计模型而不采用郭大提供的模型结构。 2. Trick 方面。这里会简单阐述一些NLP竞赛中常见的 trick。其一是 EMA,具体什么原理可以上知乎学习一下,个人使用可以提升 5k,网上有许多种不同的 EMA 的实现版本,可以多多尝试,直到找到有用的那个。其二是对抗训练,FGM、PGD、AWP 等等,最简单的是 FGM 和 PGD,这两个通过对 bert 的word embeddings 层进行微小的扰动来提升模型的泛化能力,具体原理上网查询,(对于使用的时候的一些补充,可以在 model 的 embedding 层进行扰动,因为 embedding 层 可以同时对 text 和 video 的 embedding 一起扰动,这显然是会比 仅对 text 的 word embeddings 扰动更有有益的)。其三是 F1 指标优化,百度也能搜到,这里简单介绍一下用法,通过对当前模型在验证集上的最优验证结果进行一个阈值搜索,得到一个可以使得 F1 值能够提升的最佳阈值(也叫做置信度 coeff, 这个 coeff 是 根据 验证集的 logits 和 labels 获取的),然后在模型对测试集预测的时候加载这个 coeff ,直接乘上预测得到的logits 即可,最后做 argmax(对于 F1 优化,本人刚才又做了下实验,发现有些时候 coeff 并不能产生正收益,具体原因不确定,可以选择不使用它)。上述 trick 的使用方式,EMA在模型训练的开始就可以使用,FGM、PGD对抗训练也是一样的。还有一个 trick 就是 swa(模型权重平均),实现方式有两种,一种的 torch 官方给的,还有一种大家可以在 github 上搜索 DEEPNER,里面有实现方式(本人使用的是这个),通过将模型训练最后几轮的权重进行平均。还有一个 Rdrop,具体可以看论文,这个东西本人在 NLP 比赛中确实都是有用的,该怎样起作用,只要把对应的 kl loss 放缩到 0.x 的量级即可。 3.预训练方面,本人这几天还在研究,之前简单做了 MLM,但是并未取得好成绩,刚才想到的原因可能是,输入预训练模型的时候,只是单纯的文本,而并未将 video feature 一起输入到预训练模型中,这使得上下游的模型输入不一致,而在 HOW TO FINETUNE BERT 这篇论文中,有提到过,使模型预训练和微调时的输入保持一致可以有效提升模型的性能,所以我觉得有效的做法可能是:使用和微调时一样的建模方式,即第一点所说的那样,通过将文本和video feature 转换为对应的 embedding,然后拼接在一起,送入到 bert 的 encoder 进行编码,最后得到一个 score,取这个 score 的 text 部分(实现方式就是用切片方法,先取得句子的长度,可以根据 text embedding 的 shape 获取,然后句子长度对 score 进行切片即可),然后再做 MLM 任务计算 loss等(或者做其他任务,如MFM、itm什么的,具体参考QQ浏览器第一名分享以及 QQ 浏览器第二名郭大的分享,他们都做了预训练任务,可以借鉴他们的,实现的话只能靠自己实现了),把预训练做成功,根据一群的反馈情况,应该都能有 1 个 百 的提升,这是很可观的。 个人认为,多多关注 kaggle 的比赛交流区,里面会有很多大佬分享相关比赛的 trick,对我们学习有很大的帮助。 后续如果可以,还会继续进行分享。 这里简单提供一下相关的代码实现: AWP:https://www.kaggle.com/code/junkoda/fast-awp FGM、PGD、EMA:https://github.com/fuxuelinwudi/2022-gaiic-track1-itmatch-baseline2022.3.2/blob/main/src/utils/classifier_utils.py(个人之前比赛使用的,里面有相应的使用方法) RDROP: https://github.com/dropreg/R-Drop 补充,陪跑大佬分享的,如果需要做预训练的话,在模型结构的设置上,使用两个 embedding 来对 text 和 video 进行映射,如果不做的话,就共用同一个,即参数共享。
2022-06-07