一、EDA方法1.同义词替换(Synonym Replacement, SR)：从句子中随机选取n个不属于停用词集的单词，并随机选择其同义词替换它们；2.随机插入(Random Insertion, RI)：随机的找出句中某个不属于停用词集的词，并求出其随机的同义词，将该同义词插入句子的一个随机位置。重复n次；3.随机交换(Random Swap, RS)：随机的选择句中两个单词并交换它们的位置。重复n次；4.随机删除(Random Deletion, RD)：以 $p$ 的概率，随机的移除句中的每个单词。第一种方式主要是利用同义替换的思想。 文中介绍了几个相关的方法，主要还是在于字、词、和句子结构层面上做数据增强。除了传统的字典、词向量相似度替换的方法，我觉得用mlm的方式去做数据增强这个点更加新颖和符合intuition。样本生成模型预测5、第二种就是通过“回译”的方法，这在阅读理解领域的榜单中一度有人使用过，比如说《attention is all you need》中在squad数据集上就使用了这个trick。6、句法级别的改写，生成句法树然后利用规则替换7.通过上述两个图，能很容易理解，就是预测mask位置的token，本身模型输出的是概率值，所以可以进行一个有效排序，而生成top k个近似的结果。8、第三种方式， Text Surface Transformation，主要是对句子进行一定规则的扩张和缩写，比如it is可以改写为it's9、随机噪声插入，这在addsent数据集的论文中使用过，可以使用随机插入、拼写错误插入、句子级别的打乱10、可以认为是一种句子之间相互增强，本质来将就是找到一个和原始句子相似的句子来替代，类似句子改写？但这句子是真实的？
一、bert1.为什么用layer normal 不用btach nomal这是一个仁者见仁智者见智的问题，这个问题没有标准的答案，2020年3月份的一片论文专门讨论了这问题《Rethinking Batch Normalization in Transformers》。1.主要问题是在前向传播和反向传播中，batch统计量和其贡献的梯度都会呈现一定的不稳定性，在使用BN的Transformer训练过程中，每个batch的均值与方差一直震荡。2.layer normalization 有助于得到一个球体空间中符合0均值1方差高斯分布的 embedding， batch normalization不具备这个功能。3.NLP中不同batch样本的信息关联性不大，而且由于不同的句子长度不同，强行归一化会损失不同样本间的差异信息，所以就没在batch维度进行归一化，而是选择LN2、HMM和CRF的区别，实体识别中为什么用CRF不用HMM1.HMM求解过程可能是局部最优，CRF可以全局最优2.HMM是生成模型，CRF是判别模型3.HMM是概率有向图，CRF是概率无向图HMM用在了 分词，CRF用在了 词性标注。3.NER的时候为什么要接入CRF，如何接入，原理是什么CRF是主流但是不是必需1.之所以在序列标注模型中引入CRF，是为了建模标注序列内部的依赖或约束，也就是套用一个可学习的CRF层来获得整个序列上最大似然的结果（与之相对的是为每个单词独立作决策）3.lstm如何缓解梯度消失梯度爆炸这个问题就在理论上，lstm如果你还记得公式，你就会发现，它所谓的门机制，也就是个sigmod，tanh函数处理了一下，由原来的累乘，变成了累加和累乘来解决梯度消失的问题。4.xgboost相比gbdt的优点1.第一，GBDT将目标函数泰勒展开到一阶，而xgboost将目标函数泰勒展开到了二阶。2.第二，GBDT是给新的基模型寻找新的拟合标签（前面加法模型的负梯度），而xgboost是给新的基模型寻找新的目标函数（目标函数关于新的基模型的二阶泰勒展开）3.。第三，xgboost加入了和叶子权重的L2正则化项，因而有利于模型获得更低的方差。4.第四，xgboost增加了自动处理缺失值特征的策略。通过把带缺失值样本分别划分到左子树或者右子树，比较两种方案下目标函数的优劣，从而自动对有缺失值的样本进行划分，无需对缺失特征进行填充预处理。5.xgboost自持并行化
一、背景自从Bert打开两阶段模式的魔法盒开关后，在这条路上，会有越来越多的同行者，Bert系列的算法层出不穷，比较注明的有RoBerta，ALBert。Bert系列的算法都属于AutoEncode LM。与之相对应的还要AutoRegressive LM。1.自回归语言模型（Autoregressive LM）定义在ELMO／BERT出来之前，大家通常讲的语言模型其实是根据上文内容预测下一个可能跟随的单词，就是常说的自左向右的语言模型任务，或者反过来也行，就是根据下文预测前面的单词，这种类型的LM被称为自回归语言模型。GPT 就是典型的自回归语言模型。ELMO尽管看上去利用了上文，也利用了下文，但是本质上仍然是自回归LM，这个跟模型具体怎么实现有关系。ELMO是做了两个方向（从左到右以及从右到左两个方向的语言模型），但是是分别有两个方向的自回归LM，然后把LSTM的两个方向的隐节点状态拼接到一起，来体现双向语言模型这个事情的。所以其实是两个自回归语言模型的拼接，本质上仍然是自回归语言模型。优缺点自回归语言模型有优点有缺点，(1)缺点:是只能利用上文或者下文的信息，不能同时利用上文和下文的信息，当然，貌似ELMO这种双向都做，然后拼接看上去能够解决这个问题，因为融合模式过于简单，所以效果其实并不是太好。(2)优点:其实跟下游NLP任务有关，比如生成类NLP任务，比如文本摘要，机器翻译等，在实际生成内容的时候，就是从左向右的，自回归语言模型天然匹配这个过程。而Bert这种DAE模式，在生成类NLP任务中，就面临训练过程和应用过程不一致的问题，导致生成类的NLP任务到目前为止都做不太好。2.自编码语言模型（Autoencoder LM）定义自回归语言模型只能根据上文预测下一个单词，或者反过来，只能根据下文预测前面一个单词。相比而言，Bert通过在输入X中随机Mask掉一部分单词，然后预训练过程的主要任务之一是根据上下文单词来预测这些被Mask掉的单词，如果你对Denoising Autoencoder比较熟悉的话，会看出，这确实是典型的DAE的思路。那些被Mask掉的单词就是在输入侧加入的所谓噪音。类似Bert这种预训练模式，被称为DAE LM。优缺点(1)优点：这种DAE LM的优缺点正好和自回归LM反过来，它能比较自然地融入双向语言模型，同时看到被预测单词的上文和下文，这是好处。(2)缺点:是啥呢？主要在输入侧引入[Mask]标记，导致预训练阶段和Fine-tuning阶段不一致的问题，因为Fine-tuning阶段是看不到[Mask]标记的。DAE吗，就要引入噪音，[Mask] 标记就是引入噪音的手段，这个正常。3.引入xlnetXLNet的出发点就是：能否融合自回归LM和DAE LM两者的优点。就是说如果站在自回归LM的角度，如何引入和双向语言模型等价的效果；如果站在DAE LM的角度看，它本身是融入双向语言模型的，如何抛掉表面的那个[Mask]标记，让预训练和Fine-tuning保持一致。当然，XLNet还讲到了一个Bert被Mask单词之间相互独立的问题，我相信这个不太重要，原因后面会说。当然，我认为这点不重要的事情，纯粹是个人观点，出错难免，看看就完了，不用较真。二、XLNet1.排列语言模型那么，怎么能够在单词Ti的上文中Contenxt_before中揉入下文Context_after的内容呢？你可以想想。XLNet是这么做的，在预训练阶段，引入Permutation Language Model的训练目标。什么意思呢？就是说，比如包含单词Ti的当前输入的句子X，由顺序的几个单词构成，比如x1,x2,x3,x4四个单词顺序构成。我们假设，其中，要预测的单词Ti是x3，位置在Position 3，要想让它能够在上文Context_before中，也就是Position 1或者Position 2的位置看到Position 4的单词x4。可以这么做：假设我们固定住x3所在位置，就是它仍然在Position 3，之后随机排列组合句子中的4个单词，在随机排列组合后的各种可能里，再选择一部分作为模型预训练的输入X。比如随机排列组合后，抽取出x4,x2，x3,x1这一个排列组合作为模型的输入X。于是，x3就能同时看到上文x2，以及下文x4的内容了。这就是XLNet的基本思想，所以说，看了这个就可以理解上面讲的它的初衷了吧：看上去仍然是个自回归的从左到右的语言模型，但是其实通过对句子中单词排列组合，把一部分Ti下文的单词排到Ti的上文位置中，于是，就看到了上文和下文，但是形式上看上去仍然是从左到右在预测后一个单词。但是要注意的是：在预训练的时候并不是先对语料进行排序，这个排序操作是在Attention过程中通过mask来实现的，预训练语料的输入还是按照正常的顺利x1,x2,x3,x4来输入的。（为什么不能用乱序的语料呢？因为在微调期间你的语料不可能是乱序的，使用的话，你的预训练和微调就出现不一致问题了）就算用了排列语言模型，为什么标准的语言模型就不行了呢？论文里面有解释：首先我们要知道的是，标准的语言模型使用的标准的Attention操作，那么为什么标准的Attention会失效呢？怎么理解呢：我们标准的语言模型(Attention)都是预测的下一个字，并不会带上要预测字的位置信息，比如，x1,x2,x3,x4，如果我们要预测t=3也就是x3的时候,我们是根据x1,x2的信息(内容和位置)去预测，并不会带上x3的位置信息。但是排列语言模型不在适应了，排列：x1,x2,x3,x4和x1,x2,x4,x3，那么如还用标准的attention，这两种排列模型预测的t=3时刻的字就没有区别了，实际情况是不一样的，因为我们输入的序列是x1,x2,x3,x4，但是在attention mask之后这两种排列是不一样的。排列：x1,x2,x3,x4在预测t=3时刻，预测的x1,x2,__,__ 是第三个位置。排列：x1,x2,x4,x3在预测t=3时刻，预测的x1,x2,__,__ 是第四个位置。为了解决这个问题，xlnet提出了双流注意力机制。2.双流自注意力机制(Two-Stream Self-Attention )内容流：content stream使用：，内容流失一个标准的Attention,对上下文和Xzt本身编码，例如要预测t时刻的内容，content用的是t之前的(不包括t)的上下文，和t时刻的内容。注意：content stream虽然包含本身单词的意思，但是预测的时候我们并不是根据content去预测的。而是用query流去预测的。查询流：query stream使用：，查询流是一个mask自己的attention，对上下文和Xzt时刻的位置编码，例如要预测t时刻的内容，query用的是t之前的(不包括t)的上下文，和t时刻的位置。注意：query stream不包含本身单词的意思，但是预测的时候是根据query去预测的。而query流的计算要用到content流。具体例子可以参考图片：在pretrain阶段，在输出端对查询流向量预测相应的token，如上图中最后一个公式。在finetune阶段，只需要内容流向量，不再需要查询流向量。3.NSP去掉4.使用transformer-xl5.Partial Prediction(部分预测)参考文献：1.https://arxiv.org/pdf/1906.08237.pdf2.https://yiyibooks.cn/nlp/XLNet/index.html
分类方式一.交换排序：1.冒泡排序，2.快速排序二.插入排序：3.直接插入排序，4.希尔排序三.选择排序：5.简单选择排序，6.堆排序四.7.归并排序五.8.基数排序python实现一、冒泡排序思路：两层循环：第一层代表最右边最大数的位置。第二层用来对比交换找出剩下的最大数def sort(arr): l = len(arr) # 遍历所有数组元素 for i in range(l): for j in range(0, l-i-1): if arr[j] > arr[j+1] : arr[j], arr[j+1] = arr[j+1], arr[j] arr = [64, 34, 25, 12, 22, 11, 90] sort(arr) print (arr) 二、快速排序1.递归排序算法思路：2.非递归排序算法思路：和递归比较像，用栈来存划分区间，在先找一个锚点，一最右边为例，通过锚点来划分子区间参考：https://zhuanlan.zhihu.com/p/60756341# -*- coding: UTF-8 -*- # 递归排序算法 def sort(b): """快速排序""" if len(b) < 2: return b # 选取基准，随便选哪个都可以，选中间的便于理解 mid = b[len(b)// 2] # 定义基准值左右两个数列 left, right = [], [] # 从原始数组中移除基准值 b.remove(mid) for item in b: # 大于基准值放右边 if item >= mid: right.append(item) else: # 小于基准值放左边 left.append(item) # 使用迭代进行比较 return sort(left) + [mid] + sort(right) #非递归排序算法 def quicksortUnRecur( nums, beg, end): if beg >= end: return nums stack = [beg, end] while stack: beg, end = stack.pop(0), stack.pop(0) if beg >= end: continue i, pivot = beg-1, nums[end] for j in range(beg, end+1): if nums[j] <= pivot: i += 1 nums[i], nums[j] = nums[j], nums[i] stack.extend([beg, i-1, i+1, end]) b = [11, 99, 33, 69, 77, 88, 55, 11, 33, 36, 39, 66, 44, 22] print sort(b)三、直接插入排序1.思路它的工作原理是通过构建有序序列，对于未排序数据，在已排序序列中从后向前扫描，找到相应位置并插入。2.两种写法有两种写法但是本质是一样的：(1)标准的：不交换设置一层循环,可以从0，也可以从1，都一样然后设一个变量key=arr[i]，key用来记录，不交换，让后加while(2)参考冒泡排序：交换设置一层循环,可以从0，也可以从1，都一样在设置一层循环，注意使用倒叙range(i-1, -1, -1)，然后进行交换(1)标准的：不交换# -*- coding: UTF-8 -*- def sort(arr): l = len(arr) for i in range(1, l): key = arr[i] j = i-1 while j >=0 and key < arr[j] : arr[j+1] = arr[j] j -= 1 arr[j+1] = key arr = [12, 11, 13, 5, 6] sort(arr) print(arr)(2)参考冒泡排序：交换# -*- coding: UTF-8 -*- def insert(arr): l = len(arr) for i in range(l): for j in range(i-1, -1, -1): if arr[j] > arr[j+1]: arr[j+1], arr[j] = arr[j], arr[j+1] else: break arr = [9, 5, 6, 7, 8 ] insert(arr) print arr四、希尔排序1.思路希尔排序，也称递减增量排序算法，是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法# -*- coding: UTF-8 -*- def shellSort(arr): n = len(arr) gap = int(n / 2) while gap > 0: for i in range(gap, n): temp = arr[i] j = i while j >= gap and arr[j - gap] > temp: arr[j] = arr[j - gap] j -= gap arr[j] = temp gap = int(gap / 2) arr = [12, 34, 54, 2, 3] shellSort(arr) print arr 五、简单选择排序1.思路选择排序（Selection sort）是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小（大）元素，存放到排序序列的起始位置，然后，再从剩余未排序元素中继续寻找最小（大）元素，然后放到已排序序列的末尾。# -*- coding: UTF-8 -*- def selectsort(arr): l = len(arr) for i in range(l): min_idx = i for j in range(i + 1, l): if arr[min_idx] > arr[j]: min_idx = j arr[i], arr[min_idx] = arr[min_idx], arr[i] arr = [64, 25, 12, 22, 11] selectsort(arr) print (arr)六、堆排序1.思路# -*- coding: UTF-8 -*- def heapify(arr, n, i): largest = i l = 2 * i + 1 # left = 2*i + 1 r = 2 * i + 2 # right = 2*i + 2 if l < n and arr[i] < arr[l]: largest = l if r < n and arr[largest] < arr[r]: largest = r if largest != i: arr[i],arr[largest] = arr[largest],arr[i] # 交换 heapify(arr, n, largest) def heapSort(arr): n = len(arr) # Build a maxheap. for i in range(n, -1, -1): heapify(arr, n, i) # 一个个交换元素 for i in range(n-1, 0, -1): arr[i], arr[0] = arr[0], arr[i] # 交换 heapify(arr, i, 0) arr = [ 12, 11, 13, 5, 6, 7] heapSort(arr) print arr七、归并排序1.思路归并排序采用分而治之的原理：一、将一个序列从中间位置分成两个序列；二、在将这两个子序列按照第一步继续二分下去；三、直到所有子序列的长度都为1，也就是不可以再二分截止。这时候再两两合并成一个有序序列即可。# -*- coding: UTF-8 -*- def merge(a, b): c = [] h = j = 0 while j < len(a) and h < len(b): if a[j] < b[h]: c.append(a[j]) j += 1 else: c.append(b[h]) h += 1 if j == len(a): for i in b[h:]: c.append(i) else: for i in a[j:]: c.append(i) return c def merge_sort(lists): if len(lists) <= 1: return lists middle = len(lists)/2 left = merge_sort(lists[:middle]) right = merge_sort(lists[middle:]) return merge(left, right) if __name__ == '__main__': a = [4, 7, 8, 3, 5, 9] print merge_sort(a) 八、基数排序1.思路# -*- coding: UTF-8 -*-
分类方法一、二分查找二、一、二分查找1.思路要求必须是有序列表2.两种写法1.递归查找2.非递归查找1.递归查找# -*- coding: UTF-8 -*- def binary_search1(arr, left, right, num): if left > right: #递归结束条件 return -1 mid = (left + right) // 2 if num < arr[mid]: right = mid -1 elif num > arr[mid]: left = mid + 1 else: return mid return binary_search(arr, left, right, num) #这里之所以会有return是因为必须要接收值，不然返回None #回溯到最后一层的时候，如果没有return，那么将会返回None arr = [11, 32, 51, 21, 42, 9, 5, 6, 7, 8] print(arr) arr.sort() print(arr) res = binary_search1(arr, 0, len(arr)-1,9) print(res) 2.非递归查找# -*- coding: UTF-8 -*- def binary_search(arr, num): left = 0 right = len(lis) - 1 while left <= right: #循环条件 mid = (left + right) // 2 #获取中间位置，数字的索引（序列前提是有序的） if num < arr[mid]: #如果查询数字比中间数字小，那就去二分后的左边找， right = mid - 1 #来到左边后，需要将右变的边界换为mid-1 elif num > arr[mid]: #如果查询数字比中间数字大，那么去二分后的右边找 left = mid + 1 #来到右边后，需要将左边的边界换为mid+1 else: return mid #如果查询数字刚好为中间值，返回该值得索引 return -1 #如果循环结束，左边大于了右边，代表没有找到 arr = [11, 32, 51, 21, 42, 9, 5, 6, 7, 8] print(arr) lis.sort() print(arr) res = binary_search(arr, 9) print res