词向量模型word2vec详解

“万事万物都有一个模式,它是我们宇宙的一部分。它具有对称、简洁和优雅——这些品质你总能在真正的艺术家的作品中找到。你可以在季节的更替中、在沿着山脊的沙迹中、在杂酚油灌木的树枝丛中或其叶子的图案中找到它。

我们试图在我们的生活和社会中复制这些模式,寻找令人舒适的节奏、舞蹈和形式。然而,在寻找终极完美的过程中也可能会遇到危险。显然,最终的模式有其自身的固定性。在如此完美的情况下,一切事物都走向死亡。” ~ 沙丘 (1965)

我发现Embedding(中文翻译为嵌入,但不好理解,因此后续直接使用原术语Emdedding表示)的概念是机器学习中最迷人的想法之一。如果您曾经使用过 Siri、Google Assistant、Alexa、Google Translate,甚至具有下一个单词预测功能的智能手机键盘,那么您应该能从这个已成为自然语言处理模型核心的想法中受益。

经过几十年发展,神经网络模型中的Embedding已经十分成熟(最近的发展的语境Emdedding,从而催生了BERT和 GPT等尖端模型)。

Word2vec是一种生成 Embedding 的方法,发布于2013 年。但除了作为生成embedding的方法之外,它的一些概念已经被证明可以有效地创建推荐引擎和理解时序数据。在商业、非语言任务中,像Airbnb、阿里巴巴、Spotify这样的公司都从NLP领域中提取灵感并用于产品中,从而为新型推荐引擎提供支持。

在这篇文章中,我们将讨论embedding的概念,以及使用 word2vec 生成embedding的机制。让我们从一个例子开始,熟悉使用向量来表示事物。您是否知道您的性格可以用五个数字(一个向量)的列表表示?

 

性格embedding:你是什么样的人?

如何用0到100的范围来表示你是多么内向/外向(其中0表示最内向,100表示最外向)?你是否做过诸如MBTI的人格测试,或者五大人格特质测试?这些测试会问你一系列的问题,然后在很多维度给你打分,内向/外向就是其中之一。

五大人格特质测试测试结果示例。它可以真正告诉你很多关于你自己的事情,并且在学术、人格和职业成功方面都具有预测能力。此处可以找到测试结果。

假设我的内向/外向得分为38/100。我们可以用这种方式绘图:

让我们把范围收缩到-1到1:

当你只知道这一条信息的时候,你觉得你了解这个人吗?肯定不多。人是复杂的动物。因此让我们添加另一测试的得分作为新维度。

我们可以将两个维度表示为图形上的一个点,或者作为从原点到该点的向量。我们拥有很棒的工具来处理即将上场的向量。

我已经隐藏了我们正在绘制的人格特征,这样你会渐渐习惯于在不知道每个维度代表什么的情况下,从一个人格的向量表示中获得有价值的信息。

这个向量大约可以表示我的人格。当你想要将另外两个人与我进行比较时,这种表示法就有用了。假设我被车撞了,我需要被性格相似的人替换,下图中,哪一个更像我?

处理向量时,计算相似度得分的常用方法是余弦相似度:

Person #1 在性格上与我更相似。指向相同方向的向量(长度也起作用)具有更高的余弦相似度。

两个维度的特征不足以描述不同人的信息。心理学已经研究出了五个主要人格特征(以及大量的子特征),因此,我们使用五个维度比较:

使用五个维度的问题是我们不能在二维平面绘制整齐小箭头了。这是机器学习中的挑战,我们必须要在更高维的空间中思考。但好在余弦相似度仍然有效,它适用于任意维度:

余弦相似度适用于任意数量的维度。这些得分比上次的得分要更好,因为它们是根据被比较事物的更高维度算出的。

在本节的最后,我希望提出两个中心思想:

1.我们可以将人和事物表示为代数向量(这对机器来说很棒!)。

2.我们可以很容易地计算出相似的向量之间的相互关系。

 

Embedding

“语言的天赋就是欺骗和幻觉的天赋”~《沙丘之子》

有了这种理解,我们就可以继续查看训练好的词向量实例(也被称为embedding)并开始探索它们的一些有趣属性。

这是一个单词“king”的词嵌入(在维基百科上训练的GloVe向量):

[ 0.50451 , 0.68607 , -0.59517 , -0.022801, 0.60046 , -0.13498 , -0.08813 , 0.47377 , -0.61798 , -0.31012 , -0.076666, 1.493 , -0.034189, -0.98173 , 0.68229 , 0.81722 , -0.51874 , -0.31503 , -0.55809 , 0.66421 , 0.1961 , -0.13495 , -0.11476 , -0.30344 , 0.41177 , -2.223 , -1.0756 , -1.0783 , -0.34354 , 0.33505 , 1.9927 , -0.04234 , -0.64319 , 0.71125 , 0.49159 , 0.16754 , 0.34344 , -0.25663 , -0.8523 , 0.1661 , 0.40102 , 1.1685 , -1.0137 , -0.21585 , -0.15155 , 0.78321 , -0.91241 , -1.6106 , -0.64426 , -0.51042 ]

这是一个包含50个数字的列表。直接看数字看可能看不出什么。然而如果把这些数据可视化之后,可能就不一样了。

首先,我们把所有这些数字放在一行:

我们将值转换成颜色,(如果它们接近2则为红色,接近0则为白色,接近-2则为蓝色):

我们将忽略数字,只看颜色。比较“king”和其它单词,看看它们之间的差异:

仅凭肉眼就能发现,“Man”和“Woman”之间的相似度要远远高于它们与“King”之间的相似度。这个结果表明,这些向量已经隐含了这些单词的信息以及单词之间的关联。

这是另一个示例列表(通过垂直扫描列来查找具有相似颜色的列):

我们发现:

1.所有这些不同的单词都有一条直的红色列。它们在这个维度上是相似的(虽然我们不知道每个维度是什么)

2.你可以看到“woman”和“girl”在很多地方是相似的,“man”和“boy”也是一样

3.“boy”和“girl”也有彼此相似的地方,但这些地方却与“woman”或“man”不同。这些是否可以总结出一个模糊的“youth”概念?可能吧。

4.除了最后一个单词,所有单词都是代表人。我添加了一个对象“water”来显示类别之间的差异。你可以看到蓝色列一直向下并在 “water”的词嵌入之前停下了。

5.“king”和“queen”彼此之间相似,但它们与其它单词都不同。这些是否可以总结出一个模糊的“royalty”概念?

类比

展现enbedding奇妙属性的著名例子是类比。我们可以添加、减去词嵌入并得到有趣的结果。一个著名例子是公式:“king”-“man”+“woman”:

在python中使用Gensim库,我们可以添加和减去词向量,它会找到与结果向量最相似的单词。该图像显示了最相似的单词列表,每个单词都具有余弦相似性。

我们可以像之前一样可视化这个类比:

由“king-man + woman”生成的向量并不完全等同于“queen”,但“queen”是我们在400,000个embedding中最接近它的单词。

现在我们已经看过训练好的embedding,接下来让我们更多地了解训练过程。但在我们开始使用word2vec之前,我们需要看一下embedding的父概念:神经语言模型

语言模型

“先知不会被过去、现在和未来的幻想所困扰。语言的固定性决定了这种线性区别。先知持有语言锁的钥匙。

这不是一个机械宇宙。事件的线性进展是由观察者强加的。因果?根本不是这样的。先知说出了致命的话。你瞥见了一件“注定会发生”的事情。但预言性的瞬间释放出某种具有无限预兆和力量的东西。宇宙正在经历一场幽灵般的转变。” ~沙丘神帝

如果要举自然语言处理最典型的例子,那应该就是手机输入法中的下一单词预测功能。这是个被数十亿人每天使用上百次的功能。

下一单词预测是一项可以通过语言模型解决的任务。语言模型会获取单词列表(比如说两个词)并尝试预测它们后面的单词。

在上面屏幕截图中,我们可以认为该模型接收到两个绿色单词(thou shalt)并推荐了一组单词(“not” 就是概率最高的一个):

我们可以把这个模型想象为这个黑盒:

但事实上,该模型不会只输出一个单词。实际上,它对所有它知道的单词(模型的词库,可能有几千到几百万个单词)的按可能性打分,输入法程序会选出其中分数最高的推荐给用户。

自然语言模型的输出就是模型所知单词的概率得分,我们通常把概率按百分比表示,但是实际上,40%这样的分数在输出向量组是表示为0.4

经过训练后,早起的自然语言模型(请参考Bengio 2003)将分三步完成预测:

第一步与我们最相关,因为我们讨论的就是Embedding。模型在经过训练之后会生成一个映射单词表所有单词的矩阵。在进行预测的时候,我们的算法就是在这个映射矩阵中查询输入的单词,然后计算出预测值:

现在让我们将重点放到模型训练上,来学习一下如何构建这个映射矩阵。

语言模型训练

与其他大多数其他机器学习模型相比,语言模型有巨大有优势。这个优势是我们有丰富的文本。所有我们的书籍、文章、百科、及各种类型的文本内容都可用。相比之下,许多其他机器学习的模型开发就需要手工设计数据或者专门采集数据。

我们通过找常出现在每个单词附近的词,就能获得它们的映射关系。机制如下:

1.先是获取大量文本数据(例如所有百科内容)

2. 然后我们建立一个可以沿文本滑动的窗(例如一个窗里包含三个单词)

3. 利用这样的滑动窗就能为训练模型生成大量样本数据。

当这个窗口沿着文本滑动时,我们就能(真实地)生成一套用于模型训练的数据集。为了明确理解这个过程,我们看下滑动窗是如何处理这个短语的:

在一开始的时候,窗口锁定在句子的前三个单词上:

我们把前两个单词单做特征(feature),第三个单词单做标签(label):

这时我们就生产了数据集中的第一个样本,它会被用在我们后续的语言模型训练中。

接着,我们将窗口滑动到下一个位置并生产第二个样本:

这时第二个样本也生成了。

不用多久,我们就能得到一个较大的数据集,从数据集中我们能看到在不同的单词组后面会出现的单词:

在实际应用中,模型往往在我们滑动窗口时就已经在训练。但在逻辑上将“数据集生成”与“模型训练”分开会更清晰。除了基于神经网络的语言建模方法之外,一种称为 N-gram 的技术也常用于训练语言模型。

(请参阅:《语音和语言处理》第 3 章)。为了了解从 N-gram 到神经模型的转变如何反映在现实世界的产品中,请参阅我最喜欢的 Android 输入法 Swiftkey 2015 年的一篇博客文章。这篇文章介绍了他们的神经语言模型并展示与 N-gram 模型的差异。我喜欢这个例子,因为它向您展示了如何在营销演讲中描述Embedding的算法属性。

顾及两头

根据前面的信息进行填空:

在空白前面,我提供的背景是五个单词(如果事先提及到"bus"),可以肯定,大多数人都会把bus填入空白中。但是如果我再给你一条信息——比如空白后的一个单词,会改变你的答案吗?

这下空白处该填的内容完全变了。这时"red"个词最有可能填入空白处。从这个例子中我们能学到,一个单词的前后词语都携带很有价值的信息。事实证明,我们需要考虑两个方向的单词(目标单词的左侧单词与右侧单词)。那我们该如何调整训练方式以满足这个要求呢,继续往下看。

Skipgram模型

我们不仅要考虑目标单词的前两个单词,还要考虑其后两个单词。

如果这么做,我们实际上构建并训练的模型就如下所示:

这种叫连续词袋架构(CBOW),一篇 word2vec 论文[pdf]中进描述了这种架构。

还有另一种架构,它不根据上下文(前后的单词)来猜测目标单词,而是使用当前单词来猜测相邻单词。我们设想一下滑动窗在训练数据时如下图所示:

绿框中的词语是输入词,粉框则是可能的输出结果

这里粉框颜色深度呈现不同,是因为滑动窗给训练集产生了4个独立的样本:

这种方式称为Skipgram架构。我们可以像下图这样将展示滑动窗的内容。

这样就为数据集提供了4个样本:

然后我们移动滑动窗到下一个位置:

这样我们又产生了接下来4个样本:

在移动几组位置之后,我们就能得到一批样本:

重新审视训练过程

现在我们已经从现有的文本中获得了Skipgram模型的训练数据集,接下来让我们看看如何使用它来训练一个能预测相邻词汇的自然语言模型。

从数据集中的第一个样本开始。我们将特征输入到未经训练的模型,让它预测一个可能的相邻单词。

该模型会执行三个步骤并输出预测向量(对应于单词表中每个单词的概率)。因为模型未经训练,该阶段的预测肯定是错误的。但是没关系,我们知道应该猜出的是哪个单词——这个词就是我训练集数据中的输出标签:

目标单词概率为1,其他所有单词概率为0,这样数值组成的向量就是“目标向量”。

将两个向量相减,就能得到偏差向量:

现在这一误差向量可以被用于更新模型了,所以在下一轮预测中,如果用not作为输入,我们更有可能得到thou作为输出了。

这其实就是训练的第一步了。我们接下来继续对数据集内下一份样本进行同样的操作,直到我们遍历所有的样本。这就是一轮(epoch)了。我们再多做几轮(epoch),得到训练过的模型,于是就可以从中提取嵌入矩阵来用于其他应用了。

以上确实有助于我们理解整个流程,但这依然不是word2vec真正训练的方法。我们错过了一些关键的想法。

负样本

回想一下这个神经语言模型计算预测值的三个步骤:

从计算的角度来看,第三步非常昂贵,尤其是我们需要在所有样本所执行一次(很容易就多达数千万次),因此我们需要提升性能。

一种方法是将目标分为两个步骤:

  1. 生成高质量的Embedding(不关注下一个单词预测)。
  2. 使用这些高质量的嵌入来训练语言模型(预测下一个单词)。

在本文中我们将专注于第1步(因为这篇文章专注于Embedding)。要使用高性能模型生成高质量Embedding,我们可以改变一下预测相邻单词这一任务:

将其切换到一个提取输入与输出单词的模型,输出一个表示它们是否相邻的分数(0表示“不相邻”,1表示“相邻”)。

这个简单的变换将我们需要的模型从神经网络改为逻辑回归模型——因此它变得更简单,计算速度更快。

此时我们需要改变数据集的结构——标签值现在是一个值为0或1的新列。它们将全部为1,因为我们添加的所有单词都是相邻的。

现在的计算速度可以说是大大提升!几分钟内就能处理数百万个样本。但是我们还需要解决一个漏洞。如果所有的样本都是相邻的单词(目标:1),我们这个”天才模型“可能会被训练得永远返回1——准确率是100%!但它什么东西都学不到,只会产生垃圾Embedding!

为了解决这个问题,我们需要在数据集中引入负样本 - 不相邻的单词样本。我们的模型需要为这些样本返回0。模型必须努力解决这个挑战——而且依然必须保持高速。

对于我们数据集中的每个样本,我们添加了负面示例。它们具有相同的输入字词,标签为0。

但是我们作为输出词填写什么呢?我们从词汇表中随机抽取单词

这个想法受到噪声对比估计[pdf] 的启发。我们将实际信号(相邻单词的正样本)与噪声(随机选择的不相邻的单词)进行对比。这导致了计算和统计效率的巨大折衷。

带有负例采样的Skipgram(SGNS)

 

我们现在已经介绍了word2vec中的两个(一对)核心思想:负例采样,以及skipgram。

Word2vec训练流程

现在我们已经了解了skipgram和负例采样的两个核心思想,可以继续仔细研究实际的word2vec训练过程了。

在训练过程开始之前,我们预先处理我们正在训练模型的文本。在这一步中,我们确定一下词典的大小(我们称之为vocab_size,比如说10,000)以及哪些词被它包含在内。

在训练阶段的开始,我们创建两个矩阵——Embedding矩阵和Context矩阵。这两个矩阵包含了了我们词典的所有Embedding(所以vocab_size是他们的维度之一)。第二个维度是我们希望每次嵌入的长度(embedding_size——300是一个常见值,但我们在前文也看过50的例子)。

在训练过程开始时,我们用随机值初始化这些矩阵,然后开始训练。在每个训练步骤中,我们采取一个相邻的例子及其相关的非相邻例子。我们来看看我们的第一组:

现在我们有四个单词:输入单词not和输出/上下文单词: thou(实际相邻的单词),aaron和taco(负面例子)。我们继续查找它们的Embedding——对于输入词,我们查看Embedding矩阵。对于上下文单词,我们查看Context矩阵(即使两个矩阵都在我们的词汇表中嵌入了每个单词)。

然后,我们计算输入Embedding与每个Context Embedding的点积。在每种情况下,结果都将是表示输入和上下文Embedding的相似性的数字。

现在我们需要一种方法将这些分数转化为看起来像概率的东西——我们需要它们都是正值,并且 处于0到1之间。sigmoid这一逻辑函数转换正适合用来做这样的事情啦。

现在我们可以将sigmoid操作的输出视为这些样本的模型输出。您可以看到,sigmoid操作前后,taco得分总是最高,aaron总是最低。

既然未经训练的模型已做出预测,而且我们确实拥有真实目标标签来作对比,那么让我们计算模型预测中的误差吧。为此我们只需从目标标签(label)中减去sigmoid分数。

error = target - sigmoid_scores

这是“机器学习”的“学习”部分。现在,我们可以利用这个错误分数来调整not、thou、aaron和taco的嵌入,使我们下一次做出这一计算时,结果会更接近目标分数。

训练步骤到此结束。我们从中得到了这一步所使用词语更好一些的嵌入(not,thou,aaron和taco)。我们现在进行下一步(下一个相邻样本及其相关的非相邻样本),并再次执行相同的过程。

当我们循环遍历整个数据集多次时,Embedding会逐渐收敛。当收敛到一定程度或者训练轮次达到我们预设的最大值时,我们就可以停止训练,丢弃Context矩阵。这个训练好的Embedding矩阵就可以作为一项预测任务的Embedding了。

窗口大小和负样本数量

word2vec训练过程中的两个关键超参是窗口大小和负样本的数量。

不同的任务适合不同的窗口大小。一种启发式方法是,使用较小的窗口大小(2-15)会得到这样的Embedding:两个Embedding之间的高相似性得分表明这些单词是可互换的(注意,如果我们只查看附近距离很近的单词,反义词通常也可以互换——例如,好的和坏的经常出现在类似的语境中)。使用较大的窗口大小(15-50,甚至更多)其中相似性更能表明单词的相关性。在实际操作中,你通常需要对Embedding过程提供注释以帮助读者得到相似的”语感“。Gensim默认窗口大小为5(除了输入字本身以外还包括输入字之前与之后的两个字)。

负样本的数量是训练训练过程的另一个因素。原始论文认为5-20个负样本是比较理想的数量。它还指出,当你拥有足够大的数据集时,2-5个似乎就已经足够了。Gensim默认为5个负样本。

结论

我希望您现在对Embedding和word2vec算法有所了解。我也希望现在当你读到一篇提到“带有负例采样的skipgram”(SGNS)的论文时,你已经对这些概念有了更好的认识。

参考文档

文章翻译自:https://jalammar.github.io/illustrated-word2vec/,有改动。

你可能还喜欢下面这些文章

2020年11月19日股市涨跌预测

上一篇预测股市涨跌的翻车了!毕竟概率在这儿,70%-80%的的概率毕竟不能保证一定是正确的。今天沪深300指数上午上涨,下午开始下跌,最终收跌-0.06%,感觉还好!今天晚上用之前训练好的模型去预测,结果看起来还比较乐观,如图:虽然模型也没能给出明确的涨还是跌,但看起来涨的概率还是比跌的概率稍微大一点点。此外,从我个人的主观感觉来看,明天沪深300上涨的概率也比较大,毕竟前值是下跌的趋势,而最近几天基本跌不下去了。模型说明简单说一下模型里面的数字都是什么意思吧。这些模型是根据过去2年的沪深300的波动特征训练得到的模型,上面的精准度代表预测正确的次数/总次数,比如model_6,精准度为0.7

c++ vector取最后一个元素

在C++中,你可以使用的成员函数来获取最后一个元素。这个函数返回对向量中最后一个元素的引用。以下是一个简单的示例:在这个例子中,我们创建了一个包含五个整数的。然后,我们使用函数获取最后一个元素,并将其存储在变量中。最后,我们打印出这个元素。请注意,如果向量是空的(即,不包含任何元素),调用函数将导致未定义行为。因此,在调用之前,最好先检查向量是否为空,这可以通过调用成员函数来完成。

如何选择特征

特征工程是数据分析中最耗时间和精力的一部分工作,它不像算法和模型那样是确定的步骤,更多是工程上的经验和权衡。因此没有统一的方法。这里只是对一些常用的方法做一个总结。本文关注于特征选择部分。后面还有两篇会关注于特征表达和特征预处理。1. 特征的来源在做数据分析的时候,特征的来源一般有两块,一块是业务已经整理好各种特征数据,我们需要去找出适合我们问题需要的特征;另一块是我们从业务特征中自己去寻找高级数据特征。我们就针对这两部分来分别讨论。2.  选择合适的特征我们首先看当业务已经整理好各种特征数据时,我们如何去找出适合我们问题需要的特征,此时特征数可能成百上千,哪些才是我们需要的呢?第一

股市涨跌的秘密

娱乐之作,大家见笑。^ - ^本文利用神经网络对股市的预测结果作为分析的对象,打开神经网络的黑箱,找到股市中涨跌的秘密。量价特征想要预测股市涨跌,就需要了解在股票上涨和下跌的时候,前一天发生了什么。就好像我们想要预测明天天气的时候,总会想尽办法找到过去几十年甚至几百年下雨的前一段时间都有哪些征兆。预测股票也一样,我们需要想尽一切办法找到某只股票过去几年里面价格上涨的前一天都有哪些特征,越全面越好。找特征不是一件简单的事情,有效的特征可以为我们增加预测的精准度,而无效的特征会对训练造成干扰。首先从最简单的量价特征开始,即今天的股票的价格变化和交易量变化。为什么是这两个特征?我的理论依据是市场所有

linux shell 入门

从程序员的角度来看, Shell本身是一种用C语言编写的程序,从用户的角度来看,Shell是用户与Linux操作系统沟通的桥梁。用户既可以输入命令执行,又可以利用 Shell脚本编程,完成更加复杂的操作。在Linux GUI日益完善的今天,在系统管理等领域,Shell编程仍然起着不可忽视的作用。深入地了解和熟练地掌握Shell编程,是每一个Linux用户的必修 功课之一。Linux的Shell种类众多,常见的有:Bourne Shell(/usr/bin/sh或/bin/sh)、Bourne Again Shell(/bin/bash)、C Shell(/usr/bin/csh)、K Shel

还能这样?把 Python 自动翻译成 C++

一、问题背景随着深度学习的广泛应用,在搜索引擎/推荐系统/机器视觉等业务系统中,越来越多的深度学习模型部署到线上服务。机器学习模型在离线训练时,一般要将输入的数据做特征工程预处理,再输入模型在 TensorFlow PyTorch 等框架上做训练。1.常见的特征工程逻辑常见的特征工程逻辑有: 分箱/分桶 离散化 log/exp 对数/幂等 math numpy 常见数学运算 特征缩放/归一化/截断 交叉特征生成 分词匹配程度计算 字符串分隔匹配判断 tong 缺省值填充等 数据平滑 onehot 编码,hash 编码等这些特征工程代码,当然一般使用深度学习最主要的语言 pyt

计算机语言学习指南

这篇文章讨论基于语言的基本要素,如何快速入门一种计算机语言。是一篇语言从学习到使用的指导手册,并且这种学习方式是一个系统的学习,相比于碎片化的学习,这种学习更加不容易遗忘。语言的基本成分语言的基本成分为数据、运算、控制、传输。想想你学过的语言,是不是都是这样。归结语言的组成成分,学习一门语言可以从这四个方面下手,这四个方面掌握之后,对这个语言就有个最基本的了解了。语言基本成分:数据数据是程序操作的对象。实际上我们可以思考,一个数据拥有的属性有哪些,根据我们已经掌握的语言来说(比如PHP)。$a = 1$a是数据,那么这个数据有哪些属性呢?名称(a),类型(int)。从这一行代码只能发现这两个属

gcc/g++编译参数详解

编译步骤gcc 与 g++ 分别是 gnu 的 c & c++ 编译器。gcc/g++ 在执行编译工作的时候,总共需要4步:预处理,生成 .i 的文件将预处理后的文件转换成汇编语言, 生成文件 .s 有汇编变为目标代码(机器代码)生成 .o 的文件连接目标代码, 生成可执行程序 参数详解-x language filename参数含义为指定文件所使用的语言。根据约定,C语言的后缀名称为".c",而 C++ 的后缀名为".cpp"或".cc",但如果你的源代码后缀不约定的那几种,那么需要使用-x参数来指定文件所使用的语言。这个参数对他后面的文件名都起作用。 可以使用的参数吗有下面的这些:

websocket协议详解

近来项目中使用websocket,于是来研究一番。websocket传输协议有两个部分,握手和数据传输握手GET / HTTP/1.1HOST: <IP>:<PORT> Sec-Websocket-Version: 13Sec-Websocket-Key: <KEY>Connection: keep-alive, UpgradeUpgrade: websocket之后服务端会返回类似下面的数据HTTP/1.1 101 Switching ProtocolsUpgrade: websocketConnection: UpgradeSec-WebSocket-A

使用sublime+platuml高效画图

程序员难免要经常画流程图,状态图,时序图等。以前经常用 visio 画,经常为矩形画多大,摆放在哪等问题费脑筋。有时候修改文字后,为了较好的显示效果不得不再去修改图形。今天介绍的工具是如何使用 Sublime + PlantUML 的插件画流程图,状态图,时序图等。这是一种程序员看了就会爱上的画图方式:自然,高效。什么是 PlantUMLPlantUML 是一个画图脚本语言,用它可以快速地画出:时序图流程图用例图状态图组件图简单地讲,我们使用 visio 画图时需要一个一个图去画,但使用 PlantUML 只需要用文字表达出图的内容,然后就可以直接生成图片。看一个最简单的例子:软件安装这些软件

赞赏

微信赞赏支付宝赞赏

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注