diff --git a/doc/images/Praat-output-showing-forced-alignment-for-Mae-hen-wlad-fy-nhadau-the-first-line-of-the.png b/doc/images/Praat-output-showing-forced-alignment-for-Mae-hen-wlad-fy-nhadau-the-first-line-of-the.png deleted file mode 100644 index 7bfb7fb3..00000000 Binary files a/doc/images/Praat-output-showing-forced-alignment-for-Mae-hen-wlad-fy-nhadau-the-first-line-of-the.png and /dev/null differ diff --git a/doc/images/jieba_tags.png b/doc/images/jieba_tags.png deleted file mode 100644 index a74a4ac8..00000000 Binary files a/doc/images/jieba_tags.png and /dev/null differ diff --git a/doc/images/prefix_beam_search.pdf b/doc/images/prefix_beam_search.pdf deleted file mode 100644 index f28a7b6e..00000000 Binary files a/doc/images/prefix_beam_search.pdf and /dev/null differ diff --git a/doc/images/prosody.jpeg b/doc/images/prosody.jpeg deleted file mode 100644 index 91a0ca81..00000000 Binary files a/doc/images/prosody.jpeg and /dev/null differ diff --git a/doc/src/alignment.md b/doc/src/alignment.md deleted file mode 100644 index 9d3231c8..00000000 --- a/doc/src/alignment.md +++ /dev/null @@ -1,20 +0,0 @@ -# Alignment - -我们首先从建模的角度理解一下对齐。语音识别任务,需要对输入音频序列 X = [x1,x2,x3...,xt...,xT] (通常是 fbank 或 mfcc 等音频特征)和输出的标注数据文本序列 Y = [y1,y2,y3...,yu...,yU] 关系进行建模,其中 X 的长度一般大于 Y 的长度。如果能够知道yu和xt的对应关系,就可以将这类任务变成语音帧级别上的分类任务,即对每个时刻 xt 进行分类得到 yu。 - -## MFA - - - - - -## CTC Alignment - - - - - -## Reference - -* [ctc alignment](https://mp.weixin.qq.com/s/4aGehNN7PpIvCh03qTT5oA) -* [时间戳和N-Best](https://mp.weixin.qq.com/s?__biz=MzU2NjUwMTgxOQ==&mid=2247483956&idx=1&sn=80ce595238d84155d50f08c0d52267d3&chksm=fcaacae0cbdd43f62b1da60c8e8671a9e0bb2aeee94f58751839b03a1c45b9a3889b96705080&scene=21#wechat_redirect) diff --git a/doc/src/asr_text_backend.md b/doc/src/asr_text_backend.md deleted file mode 100644 index c3c9896c..00000000 --- a/doc/src/asr_text_backend.md +++ /dev/null @@ -1,101 +0,0 @@ -# ASR Text Backend - -1. [Text Segmentation](text_front_end#text segmentation) -2. Text Corrector -3. Add Punctuation -4. Text Filter - - - -## Text Corrector - -* [pycorrector](https://github.com/shibing624/pycorrector) - 本项目重点解决其中的谐音、混淆音、形似字错误、中文拼音全拼、语法错误带来的纠错任务。PS:[网友源码解读](https://zhuanlan.zhihu.com/p/138981644) -* DeepCorrection [1](https://praneethbedapudi.medium.com/deepcorrection-1-sentence-segmentation-of-unpunctuated-text-a1dbc0db4e98) [2](https://praneethbedapudi.medium.com/deepcorrection2-automatic-punctuation-restoration-ac4a837d92d9) [3](https://praneethbedapudi.medium.com/deepcorrection-3-spell-correction-and-simple-grammar-correction-d033a52bc11d) [4](https://praneethbedapudi.medium.com/deepsegment-2-0-multilingual-text-segmentation-with-vector-alignment-fd76ce62194f) - - - -### Question - -中文文本纠错任务,常见错误类型包括: - -- 谐音字词,如 配副眼睛-配副眼镜 -- 混淆音字词,如 流浪织女-牛郎织女 -- 字词顺序颠倒,如 伍迪艾伦-艾伦伍迪 -- 字词补全,如 爱有天意-假如爱有天意 -- 形似字错误,如 高梁-高粱 -- 中文拼音全拼,如 xingfu-幸福 -- 中文拼音缩写,如 sz-深圳 -- 语法错误,如 想象难以-难以想象 - -当然,针对不同业务场景,这些问题并不一定全部存在。 - -比如输入法中需要处理前四种,搜索引擎需要处理所有类型,语音识别后文本纠错只需要处理前两种, 其中'形似字错误'主要针对五笔或者笔画手写输入等。 - - - -### Solution - -#### 规则的解决思路 - -1. 中文纠错分为两步走,第一步是错误检测,第二步是错误纠正; -2. 错误检测部分先通过结巴中文分词器切词,由于句子中含有错别字,所以切词结果往往会有切分错误的情况,这样从字粒度和词粒度两方面检测错误, 整合这两种粒度的疑似错误结果,形成疑似错误位置候选集; -3. 错误纠正部分,是遍历所有的疑似错误位置,并使用音似、形似词典替换错误位置的词,然后通过语言模型计算句子困惑度,对所有候选集结果比较并排序,得到最优纠正词。 - -#### 深度模型的解决思路 - -1. 端到端的深度模型可以避免人工提取特征,减少人工工作量,RNN序列模型对文本任务拟合能力强,rnn_attention在英文文本纠错比赛中取得第一名成绩,证明应用效果不错; -2. CRF会计算全局最优输出节点的条件概率,对句子中特定错误类型的检测,会根据整句话判定该错误,阿里参赛2016中文语法纠错任务并取得第一名,证明应用效果不错; -3. Seq2Seq模型是使用Encoder-Decoder结构解决序列转换问题,目前在序列转换任务中(如机器翻译、对话生成、文本摘要、图像描述)使用最广泛、效果最好的模型之一; -4. BERT/ELECTRA/ERNIE/MacBERT等预训练模型强大的语言表征能力,对NLP界带来翻天覆地的改变,海量的训练数据拟合的语言模型效果无与伦比,基于其MASK掩码的特征,可以简单改造预训练模型用于纠错,加上fine-tune,效果轻松达到最优。 - - - -### 规则检测方法 - -- kenlm:kenlm统计语言模型工具,规则方法,语言模型纠错,利用混淆集,扩展性强 - -#### 错误检测 - -- 字粒度:语言模型困惑度(ppl)检测某字的似然概率值低于句子文本平均值,则判定该字是疑似错别字的概率大。 -- 词粒度:切词后不在词典中的词是疑似错词的概率大。 - -#### 错误纠正 - -- 通过错误检测定位所有疑似错误后,取所有疑似错字的音似、形似候选词, -- 使用候选词替换,基于语言模型得到类似翻译模型的候选排序结果,得到最优纠正词。 - -#### 思考 - -1. 现在的处理手段,在词粒度的错误召回还不错,但错误纠正的准确率还有待提高,更多优质的纠错集及纠错词库会有提升。 -2. 另外,现在的文本错误不再局限于字词粒度上的拼写错误,需要提高中文语法错误检测(CGED, Chinese Grammar Error Diagnosis)及纠正能力。 - - - -### Reference - -* https://github.com/shibing624/pycorrector -* [基于文法模型的中文纠错系统](https://blog.csdn.net/mingzai624/article/details/82390382) -* [Norvig’s spelling corrector](http://norvig.com/spell-correct.html) -* [Chinese Spelling Error Detection and Correction Based on Language Model, Pronunciation, and Shape[Yu, 2013]](http://www.aclweb.org/anthology/W/W14/W14-6835.pdf) -* [Chinese Spelling Checker Based on Statistical Machine Translation[Chiu, 2013]](http://www.aclweb.org/anthology/O/O13/O13-1005.pdf) -* [Chinese Word Spelling Correction Based on Rule Induction[yeh, 2014]](http://aclweb.org/anthology/W14-6822) -* [Neural Language Correction with Character-Based Attention[Ziang Xie, 2016]](https://arxiv.org/pdf/1603.09727.pdf) -* [Chinese Spelling Check System Based on Tri-gram Model[Qiang Huang, 2014]](http://www.anthology.aclweb.org/W/W14/W14-6827.pdf) -* [Neural Abstractive Text Summarization with Sequence-to-Sequence Models[Tian Shi, 2018]](https://arxiv.org/abs/1812.02303) -* [基于深度学习的中文文本自动校对研究与实现[杨宗霖, 2019]](https://github.com/shibing624/pycorrector/blob/master/docs/基于深度学习的中文文本自动校对研究与实现.pdf) -* [A Sequence to Sequence Learning for Chinese Grammatical Error Correction[Hongkai Ren, 2018]](https://link.springer.com/chapter/10.1007/978-3-319-99501-4_36) -* [ELECTRA: Pre-training Text Encoders as Discriminators Rather Than Generators](https://openreview.net/pdf?id=r1xMH1BtvB) -* [Revisiting Pre-trained Models for Chinese Natural Language Processing](https://arxiv.org/abs/2004.13922) - - - -## Add Punctuation - -* DeepCorrection [1](https://praneethbedapudi.medium.com/deepcorrection-1-sentence-segmentation-of-unpunctuated-text-a1dbc0db4e98) [2](https://praneethbedapudi.medium.com/deepcorrection2-automatic-punctuation-restoration-ac4a837d92d9) [3](https://praneethbedapudi.medium.com/deepcorrection-3-spell-correction-and-simple-grammar-correction-d033a52bc11d) [4](https://praneethbedapudi.medium.com/deepsegment-2-0-multilingual-text-segmentation-with-vector-alignment-fd76ce62194f) - - - -## Text Filter - -* 敏感词(黄暴、涉政、违法违禁等) diff --git a/doc/src/chinese_syllable.md b/doc/src/chinese_syllable.md deleted file mode 100644 index 3ada44f4..00000000 --- a/doc/src/chinese_syllable.md +++ /dev/null @@ -1,70 +0,0 @@ -# chinese syllable - - - -## Syllable - -* [List of Syllables in Pinyin](https://resources.allsetlearning.com/chinese/pronunciation/syllable) - The word syllable is a term referring to the units of a word, composed on an (optional) initial, a final, and a tone. - - The word "syllable" is 音节 (yīnjié) in Chinese. - - Most spoken syllables in Mandarin Chinese correspond to one written Chinese character. - - There are a total of 410 common pinyin syllables. - -* [Rare syllable](https://resources.allsetlearning.com/chinese/pronunciation/Rare_syllable) - -* [Chinese Pronunciation: The Complete Guide for Beginner](https://www.digmandarin.com/chinese-pronunciation-guide.html) - -* [Mandarin Chinese Phonetics](http://www.zein.se/patrick/chinen8p.html) - -* [chinese phonetics](https://www.easymandarin.cn/online-chinese-lessons/chinese-phonetics/) - Chinese Characters, called “Hanzi”, are the writing symbols of the Chinese language. - Pinyin is the Romanization of a phonetic notation for Chinese Characters. - Each syllable is composed of three parts: initials, finals, and tones. - In the Pinyin system there are 23 initials, 24 finals, 4 tones and a neutral tone. - - - -## Pinyin -* [Pinyin](https://en.wikipedia.org/wiki/Pinyin) -* [Pinyin quick start guide](https://resources.allsetlearning.com/chinese/pronunciation/Pinyin_quick_start_guide) -* [Pinyin Table](https://en.wikipedia.org/wiki/Pinyin_table) -* [Piyin Chat](https://resources.allsetlearning.com/chinese/pronunciation/Pinyin_chart) -* [Mandarin Chinese Pinyin Table](https://www.archchinese.com/chinese_pinyin.html) -* [Chinese Pinyin Table ](http://www.quickmandarin.com/chinesepinyintable/) - - - -## Tones -* [Four tones](https://resources.allsetlearning.com/chinese/pronunciation/Four_tones) -* [Neutral tone](https://resources.allsetlearning.com/chinese/pronunciation/Neutral_tone) -* [Where do the tone marks go?](http://www.pinyin.info/rules/where.html) -* [声调符号标在哪儿?](http://www.hwjyw.com/resource/content/2010/06/04/8183.shtml) - - - -## Zhuyin - -* [Bopomofo](https://en.wikipedia.org/wiki/Bopomofo) -* [Zhuyin table](https://en.wikipedia.org/wiki/Zhuyin_table) - - - -## Tone sandhi - -* https://zh.wikipedia.org/wiki/%E8%AE%8A%E8%AA%BF - -* https://github.com/mozillazg/python-pinyin/issues/133 - - pypinyin关于变调错误的评估 - - - -## tools - -* https://github.com/KuangDD/phkit -* https://github.com/mozillazg/python-pinyin -* https://github.com/Kyubyong/g2pC -* https://github.com/kakaobrain/g2pM diff --git a/doc/src/crf.md b/doc/src/crf.md deleted file mode 100644 index faff2ead..00000000 --- a/doc/src/crf.md +++ /dev/null @@ -1,29 +0,0 @@ -# CRF([Conditional Random Fields](http://blog.echen.me/2012/01/03/introduction-to-conditional-random-fields/)) - -## Repos - -* https://github.com/kmkurn/pytorch-crf -* https://github.com/allenai/allennlp -* https://github.com/mtreviso/linear-chain-crf - -## Reference - -* [Overview of Conditional Random Fields](https://medium.com/ml2vec/overview-of-conditional-random-fields-68a2a20fa541) -* [Introduction to Conditional Random Fields](http://blog.echen.me/2012/01/03/introduction-to-conditional-random-fields/)] -* [Viterbi algorithm](https://en.wikipedia.org/wiki/Viterbi_algorithm) -* [Conditional Random Field Tutorial in PyTorch](https://towardsdatascience.com/conditional-random-field-tutorial-in-pytorch-ca0d04499463) -* [Implementing a linear-chain Conditional Random Field (CRF) in PyTorch](https://towardsdatascience.com/implementing-a-linear-chain-conditional-random-field-crf-in-pytorch-16b0b9c4b4ea) - -* .https://homepages.inf.ed.ac.uk/csutton/publications/crftutv2.pdf -* [Implementing a linear-chain Conditional Random Field (CRF) in PyTorch](https://towardsdatascience.com/implementing-a-linear-chain-conditional-random-field-crf-in-pytorch-16b0b9c4b4ea) - - - -* http://www.cs.columbia.edu/~mcollins/ - -* [Tagging Problems, and Hidden Markov Models](http://www.cs.columbia.edu/~mcollins/hmms-spring2013.pdf) -* http://www.cs.columbia.edu/~mcollins/crf.pdf -* [The Forward-Backward Algorithm](http://www.cs.columbia.edu/~mcollins/fb.pdf) - -* [The Naive Bayes Model, Maximum-Likelihood Estimation, and the EM Algorithm](http://www.cs.columbia.edu/~mcollins/em.pdf) - diff --git a/doc/src/dataset.md b/doc/src/dataset.md deleted file mode 100644 index aaa80551..00000000 --- a/doc/src/dataset.md +++ /dev/null @@ -1,21 +0,0 @@ -# Dataset - -## Text - -* [Tatoeba](https://tatoeba.org/cmn) - - **Tatoeba is a collection of sentences and translations.** It's collaborative, open, free and even addictive. An open data initiative aimed at translation and speech recognition. - - - -## Speech - -* [Tatoeba](https://tatoeba.org/cmn) - - **Tatoeba is a collection of sentences and translations.** It's collaborative, open, free and even addictive. An open data initiative aimed at translation and speech recognition. - - - -### ASR Noise - -* [asr-noises](https://github.com/speechio/asr-noises) diff --git a/doc/src/decoding.md b/doc/src/decoding.md deleted file mode 100644 index 6eb77dd3..00000000 --- a/doc/src/decoding.md +++ /dev/null @@ -1,10 +0,0 @@ -# Decoding - -## Reference - -* [时间戳和N-Best](https://mp.weixin.qq.com/s?__biz=MzU2NjUwMTgxOQ==&mid=2247483956&idx=1&sn=80ce595238d84155d50f08c0d52267d3&chksm=fcaacae0cbdd43f62b1da60c8e8671a9e0bb2aeee94f58751839b03a1c45b9a3889b96705080&scene=21#wechat_redirect) - -* [Viterbi algorithm](https://en.wikipedia.org/wiki/Viterbi_algorithm) - -* [如何通俗地讲解 viterbi 算法](https://www.zhihu.com/question/20136144) - diff --git a/doc/src/eigen.md b/doc/src/eigen.md deleted file mode 100644 index d2dd3282..00000000 --- a/doc/src/eigen.md +++ /dev/null @@ -1,2046 +0,0 @@ -# Eigen - -http://eigen.tuxfamily.org/dox/index.html - -## 简介 - -Eigen是C++中可以用来调用并进行矩阵计算的一个库,简单了说它就是一个c++版本的matlab包。 - -## 安装 - -下载eigen:http://eigen.tuxfamily.org/index.php?title=Main_Page#Download - -Eigen只包含头文件,因此它不需要实现编译,只需要你include到你的项目,指定好Eigen的头文件路径,编译项目即可。而且跨平台,当然这是必须的。 - -**方案一** - -下载后,解压得到文件夹中,Eigen子文件夹便是我们需要的全部;如果你想使用Eigen暂不支持的特性,可以使用unsupported子文件夹。可以把Eigen/unsupported复制到任何你需要的地方。 - -**方案二** - -安装改包,其实就是把Eigen/unsupported的内容复制到“/usr/local/include/eigen3”下。在解压的文件夹下,新建build_dir,执行。 - -``` - cd build_dir - cmake ../ - make install -``` - -详见INSTALL文件即可。 - -## 模块和头文件 - -Eigen库被分为一个Core模块和其他一些模块,每个模块有一些相应的头文件。 为了便于引用,Dense模块整合了一系列模块;Eigen模块整合了所有模块。一般情况下,`include` 就够了。 - -| Module | Header file | Contents | -| ----------- | --------------------------- | ------------------------------------------------------ | -| Core | #include | Matrix和Array类,基础的线性代数运算和数组操作 | -| Geometry | #include | 旋转、平移、缩放、2维和3维的各种变换 | -| LU | #include | 求逆,行列式,LU分解 | -| Cholesky | #include | LLT和LDLT Cholesky分解 | -| Householder | #include | 豪斯霍尔德变换,用于线性代数运算 | -| SVD | #include | SVD分解 | -| QR | #include | QR分解 | -| Eigenvalues | #include | 特征值,特征向量分解 | -| Sparse | #include | 稀疏矩阵的存储和一些基本的线性运算 | -| 稠密矩阵 | #include | 包含了Core/Geometry/LU/Cholesky/SVD/QR/Eigenvalues模块 | -| 矩阵 | #include | 包括Dense和Sparse(整合库) | - -## 一个简单的例子 - -``` -#include -#include -using Eigen::MatrixXd; -int main() -{ - MatrixXd m(2,2); - m(0,0) = 3; - m(1,0) = 2.5; - m(0,1) = -1; - m(1,1) = m(1,0) + m(0,1); - std::cout << m << std::endl; -} -``` - -编译并执行:`g++ main.cpp -I /usr/local/include/eigen3/ -o maincpp` - -``` - 3 -1 -2.5 1.5 -``` - -Eigen头文件定义了许多类型,所有的类型都在Eigen的命名空间内。MatrixXd代表的是任意大小(X*X)的矩阵,并且每个元素为double类型。 - -## 例2: 矩阵和向量 - -再看另一个例子 - -``` -#include -#include -using namespace Eigen; -using namespace std; -int main() -{ - MatrixXd m = MatrixXd::Random(3,3); - m = (m + MatrixXd::Constant(3,3,1.2)) * 50; - cout << "m =" << endl << m << endl; - VectorXd v(3); - v << 1, 2, 3; - cout << "m * v =" << endl << m * v << endl; -} -``` - -输出为: - -``` -m = - 94 89.8 43.5 -49.4 101 86.8 -88.3 29.8 37.8 -m * v = -404 -512 -261 -``` - -程序中定义了一个任意大小的矩阵,并用3`*`3的随机阵初始化。`MatrixXd::Constant`创建一个3*3的常量矩阵。 - -VectorXd表示列向量,并用*逗号初始化语法*来初始化。 - -在看同样功能的代码 - -``` -#include -#include -using namespace Eigen; -using namespace std; -int main() -{ - Matrix3d m = Matrix3d::Random(); - m = (m + Matrix3d::Constant(1.2)) * 50; - cout << "m =" << endl << m << endl; - Vector3d v(1,2,3); - - cout << "m * v =" << endl << m * v << endl; -} -``` - -MatrixXd表示是任意尺寸的矩阵,Matrix3d直接指定了3*3的大小。Vector3d也被直接初始化为[1,2,3]'的列向量。 - -使用固定大小的矩阵或向量有两个好处:编译更快;指定大小可以进行更为严格的检查。当然使用太多类别(Matrix3d、Matrix4d、Matrix5d...)会增加编译时间和可执行文件大小,原则建议使用4及以内的。 - -## Matrix类 - -在Eigen,所有的矩阵和向量都是**Matrix**模板类的对象,Vector只是一种特殊的矩阵(一行或者一列)。 - -Matrix有6个模板参数,主要使用前三个参数,剩下的有默认值。 - -``` -Matrix -``` - -Scalar是表示元素的类型,RowsAtCompileTime为矩阵的行,ColsAtCompileTime为矩阵的列。 - -库中提供了一些类型便于使用,比如: - -``` -typedef Matrix Matrix4f; -``` - -## Vectors向量 - -列向量 - -``` -typedef Matrix Vector3f; -``` - -行向量 - -``` -typedef Matrix RowVector2i; -``` - -## Dynamic - -Eigen不只限于已知大小(编译阶段)的矩阵,有些矩阵的尺寸是运行时确定的,于是引入了一个特殊的标识符:Dynamic - -``` -typedef Matrix MatrixXd; -typedef Matrix VectorXi; -Matrix -``` - -## 构造函数 - -默认的构造函数不执行任何空间分配,也不初始化矩阵的元素。 - -``` -Matrix3f a; -MatrixXf b; -``` - -这里,a是一个3*3的矩阵,分配了float[9]的空间,但未初始化内部元素;b是一个动态大小的矩阵,定义是未分配空间(0*0)。 - -指定大小的矩阵,只是分配相应大小的空间,未初始化元素。 - -``` -MatrixXf a(10,15); -VectorXf b(30); -``` - -这里,a是一个10*15的动态大小的矩阵,分配了空间但未初始化元素;b是一个30大小的向量,同样分配空间未初始化元素。 - -为了对固定大小和动态大小的矩阵提供统一的API,对指定大小的Matrix传递sizes也是合法的(传递也被忽略)。 - -``` -Matrix3f a(3,3); -``` - -可以用构造函数提供4以内尺寸的vector的初始化。 - -``` -Vector2d a(5.0, 6.0); -Vector3d b(5.0, 6.0, 7.0); -Vector4d c(5.0, 6.0, 7.0, 8.0); -``` - -## 获取元素 - -通过中括号获取元素,对于矩阵是:(行,列);对于向量,只是传递它的索引,以0为起始。 - -``` -#include -#include -using namespace Eigen; -int main() -{ - MatrixXd m(2,2); - m(0,0) = 3; - m(1,0) = 2.5; - m(0,1) = -1; - m(1,1) = m(1,0) + m(0,1); - std::cout << "Here is the matrix m:\n" << m << std::endl; - VectorXd v(2); - v(0) = 4; - v(1) = v(0) - 1; - std::cout << "Here is the vector v:\n" << v << std::endl; -} -``` - -输出 - -``` -Here is the matrix m: - 3 -1 -2.5 1.5 -Here is the vector v: -4 -3 -``` - -m(index)也可以用于获取矩阵元素,但取决于matrix的存储顺序,默认是按列存储的,当然也可以改为按行。 - -[]操作符可以用于向量元素的获取,但是不能用于matrix,因为C++中[]不能传递超过一个参数。 - -## 逗号初始化 - -``` -Matrix3f m; -m << 1, 2, 3, - 4, 5, 6, - 7, 8, 9; -std::cout << m; -``` - -## resizing - -matrix的大小可以通过rows()、cols()、size()获取,resize()可以重新调整动态matrix的大小。 - -``` -#include -#include -using namespace Eigen; -int main() -{ - MatrixXd m(2,5); - m.resize(4,3); - std::cout << "The matrix m is of size " - << m.rows() << "x" << m.cols() << std::endl; - std::cout << "It has " << m.size() << " coefficients" << std::endl; - VectorXd v(2); - v.resize(5); - std::cout << "The vector v is of size " << v.size() << std::endl; - std::cout << "As a matrix, v is of size " - << v.rows() << "x" << v.cols() << std::endl; -} -``` - -输出: - -``` -The matrix m is of size 4x3 -It has 12 coefficients -The vector v is of size 5 -As a matrix, v is of size 5x1 -``` - -如果matrix的实际大小不改变,resize函数不做任何操作。resize操作会执行析构函数:元素的值会被改变,如果不想改变执行 conservativeResize()。 - -为了统一API,所有的操作可用于指定大小的matrix,当然,实际中它不会改变大小。尝试去改变一个固定大小的matrix到一个不同的值,会出发警告失败。只有如下是合法的。 - -``` -#include -#include -using namespace Eigen; -int main() -{ - Matrix4d m; - m.resize(4,4); // no operation - std::cout << "The matrix m is of size " - << m.rows() << "x" << m.cols() << std::endl; -} -``` - -## assignment 和 resizing - -assignment(分配)是复制一个矩阵到另外一个,操作符=。Eigen会自动resize左变量大小等于右变量大小,比如: - -``` -MatrixXf a(2,2); -std::cout << "a is of size " << a.rows() << "x" << a.cols() << std::endl; -MatrixXf b(3,3); -a = b; -std::cout << "a is now of size " << a.rows() << "x" << a.cols() << std::endl; - -a is of size 2x2 -a is now of size 3x3 -``` - -当然,如果左边量是固定大小的,上面的resizing是不允许的。 - -## 固定尺寸 vs 动态尺寸 - -实际中,应该使用固定尺寸还是动态尺寸,简单的答案是:小的尺寸用固定的,大的尺寸用动态的。使用固定尺寸可以避免动态内存的开辟,固定尺寸只是一个普通数组。 - -``` -Matrix4f mymatrix;` 等价于 `float mymatrix[16]; -MatrixXf mymatrix(rows,columns);` 等价于 `float *mymatrix = new float[rows*columns]; -``` - -使用固定尺寸(<=4*4)需要编译前知道矩阵大小,而且对于足够大的尺寸,如大于32,固定尺寸的收益可以忽略不计,而且可能导致栈崩溃。而且基于环境,Eigen会对动态尺寸做优化(类似于std::vector) - -## 其他模板参数 - -上面只讨论了前三个参数,完整的模板参数如下: - -``` -Matrix -``` - -Options是一个比特标志位,这里,我们只介绍一种RowMajor,它表明matrix使用按行存储,默认是按列存储。`Matrix` - -MaxRowsAtCompileTime和MaxColsAtCompileTime表示在编译阶段矩阵的上限。主要是避免动态内存分配,使用数组。 - -``` -Matrix` 等价于 `float [12] -``` - -## 一些方便的定义 - -Eigen定义了一些类型 - -- MatrixNt = Matrix 特殊地有 MatrxXi = Matrix -- VectorNt = Matrix 比如 Vector2f = Matrix -- RowVectorNt = Matrix 比如 RowVector3d = Matrix - -N可以是2,3,4或X(Dynamic) - -t可以是i(int)、f(float)、d(double)、cf(complex)、cd(complex)等。 - -# 矩阵和向量的运算 - -提供一些概述和细节:关于矩阵、向量以及标量的运算。 - -## 介绍 - -Eigen提供了matrix/vector的运算操作,既包括重载了c++的算术运算符+/-/*,也引入了一些特殊的运算比如点乘dot、叉乘cross等。 - -对于Matrix类(matrix和vectors)这些操作只支持线性代数运算,比如:matrix1*matrix2表示矩阵的乘机,vetor+scalar是不允许的。如果你想执行非线性代数操作,请看下一篇(暂时放下)。 - -## 加减 - -左右两侧变量具有相同的尺寸(行和列),并且元素类型相同(Eigen不自动转化类型)操作包括: - -- 二元运算 + 如a+b -- 二元运算 - 如a-b -- 一元运算 - 如-a -- 复合运算 += 如a+=b -- 复合运算 -= 如a-=b - -``` -#include -#include -using namespace Eigen; -int main() -{ - Matrix2d a; - a << 1, 2, - 3, 4; - MatrixXd b(2,2); - b << 2, 3, - 1, 4; - std::cout << "a + b =\n" << a + b << std::endl; - std::cout << "a - b =\n" << a - b << std::endl; - std::cout << "Doing a += b;" << std::endl; - a += b; - std::cout << "Now a =\n" << a << std::endl; - Vector3d v(1,2,3); - Vector3d w(1,0,0); - std::cout << "-v + w - v =\n" << -v + w - v << std::endl; -} -``` - -输出: - -``` -a + b = -3 5 -4 8 -a - b = --1 -1 - 2 0 -Doing a += b; -Now a = -3 5 -4 8 --v + w - v = --1 --4 --6 -``` - -## 标量乘法和除法 - -乘/除标量是非常简单的,如下: - -- 二元运算 * 如matrix*scalar -- 二元运算 * 如scalar*matrix -- 二元运算 / 如matrix/scalar -- 复合运算 *= 如matrix*=scalar -- 复合运算 /= 如matrix/=scalar - -``` -#include -#include -using namespace Eigen; -int main() -{ - Matrix2d a; - a << 1, 2, - 3, 4; - Vector3d v(1,2,3); - std::cout << "a * 2.5 =\n" << a * 2.5 << std::endl; - std::cout << "0.1 * v =\n" << 0.1 * v << std::endl; - std::cout << "Doing v *= 2;" << std::endl; - v *= 2; - std::cout << "Now v =\n" << v << std::endl; -} -``` - -结果 - -``` -a * 2.5 = -2.5 5 -7.5 10 -0.1 * v = -0.1 -0.2 -0.3 -Doing v *= 2; -Now v = -2 -4 -6 -``` - -## 表达式模板 - -这里简单介绍,在高级主题中会详细解释。在Eigen中,线性运算比如+不会对变量自身做任何操作,会返回一个“表达式对象”来描述被执行的计算。当整个表达式被评估完(一般是遇到=号),实际的操作才执行。 - -这样做主要是为了优化,比如 - -``` -VectorXf a(50), b(50), c(50), d(50); -... -a = 3*b + 4*c + 5*d; -``` - -Eigen会编译这段代码最终遍历一次即可运算完成。 - -``` -for(int i = 0; i < 50; ++i) - a[i] = 3*b[i] + 4*c[i] + 5*d[i]; -``` - -因此,我们不必要担心大的线性表达式的运算效率。 - -## 转置和共轭 - -![img](https://images2015.cnblogs.com/blog/532915/201701/532915-20170124234218331-958991885.png) 表示transpose转置 - -![img](https://images2015.cnblogs.com/blog/532915/201701/532915-20170124234219644-1435768692.png) 表示conjugate共轭 - -![img](https://images2015.cnblogs.com/blog/532915/201701/532915-20170124234220831-1674600657.png) 表示adjoint(共轭转置) 伴随矩阵 - -``` -MatrixXcf a = MatrixXcf::Random(2,2); -cout << "Here is the matrix a\n" << a << endl; -cout << "Here is the matrix a^T\n" << a.transpose() << endl; -cout << "Here is the conjugate of a\n" << a.conjugate() << endl; -cout << "Here is the matrix a^*\n" << a.adjoint() << endl; -``` - -输出 - -``` -Here is the matrix a - (-0.211,0.68) (-0.605,0.823) - (0.597,0.566) (0.536,-0.33) -Here is the matrix a^T - (-0.211,0.68) (0.597,0.566) -(-0.605,0.823) (0.536,-0.33) -Here is the conjugate of a - (-0.211,-0.68) (-0.605,-0.823) - (0.597,-0.566) (0.536,0.33) -Here is the matrix a^* - (-0.211,-0.68) (0.597,-0.566) -(-0.605,-0.823) (0.536,0.33) -``` - -对于实数矩阵,conjugate不执行任何操作,adjoint等价于transpose。 - -transpose和adjoint会简单的返回一个代理对象并不对本省做转置。如果执行 `b=a.transpose()` ,a不变,转置结果被赋值给b。如果执行 `a=a.transpose()` Eigen在转置结束之前结果会开始写入a,所以a的最终结果不一定等于a的转置。 - -``` -Matrix2i a; a << 1, 2, 3, 4; -cout << "Here is the matrix a:\n" << a << endl; -a = a.transpose(); // !!! do NOT do this !!! -cout << "and the result of the aliasing effect:\n" << a << endl; - -Here is the matrix a: -1 2 -3 4 -and the result of the aliasing effect: -1 2 -2 4 -``` - -这被称为“别名问题”。在debug模式,当assertions打开的情况加,这种常见陷阱可以被自动检测到。 - -对 `a=a.transpose()` 这种操作,可以执行in-palce转置。类似还有adjointInPlace。 - -``` -MatrixXf a(2,3); a << 1, 2, 3, 4, 5, 6; -cout << "Here is the initial matrix a:\n" << a << endl; -a.transposeInPlace(); -cout << "and after being transposed:\n" << a << endl; - -Here is the initial matrix a: -1 2 3 -4 5 6 -and after being transposed: -1 4 -2 5 -3 6 -``` - -## 矩阵-矩阵的乘法和矩阵-向量的乘法 - -向量也是一种矩阵,实质都是矩阵-矩阵的乘法。 - -- 二元运算 *如a*b -- 复合运算 *=如a*=b - -``` -#include -#include -using namespace Eigen; -int main() -{ - Matrix2d mat; - mat << 1, 2, - 3, 4; - Vector2d u(-1,1), v(2,0); - std::cout << "Here is mat*mat:\n" << mat*mat << std::endl; - std::cout << "Here is mat*u:\n" << mat*u << std::endl; - std::cout << "Here is u^T*mat:\n" << u.transpose()*mat << std::endl; - std::cout << "Here is u^T*v:\n" << u.transpose()*v << std::endl; - std::cout << "Here is u*v^T:\n" << u*v.transpose() << std::endl; - std::cout << "Let's multiply mat by itself" << std::endl; - mat = mat*mat; - std::cout << "Now mat is mat:\n" << mat << std::endl; -} -``` - -输出 - -``` -Here is mat*mat: - 7 10 -15 22 -Here is mat*u: -1 -1 -Here is u^T*mat: -2 2 -Here is u^T*v: --2 -Here is u*v^T: --2 -0 - 2 0 -Let's multiply mat by itself -Now mat is mat: - 7 10 -15 22 -``` - -`m=m*m`并不会导致别名问题,Eigen在这里做了特殊处理,引入了临时变量。实质将编译为: - -``` -tmp = m*m -m = tmp -``` - -如果你确定矩阵乘法是安全的(并没有别名问题),你可以使用noalias()函数来避免临时变量 `c.noalias() += a*b` 。 - -## 点运算和叉运算 - -dot()执行点积,cross()执行叉积,点运算得到1*1的矩阵。当然,点运算也可以用u.adjoint()*v来代替。 - -``` -#include -#include -using namespace Eigen; -using namespace std; -int main() -{ - Vector3d v(1,2,3); - Vector3d w(0,1,2); - cout << "Dot product: " << v.dot(w) << endl; - double dp = v.adjoint()*w; // automatic conversion of the inner product to a scalar - cout << "Dot product via a matrix product: " << dp << endl; - cout << "Cross product:\n" << v.cross(w) << endl; -} -``` - -输出 - -``` -Dot product: 8 -Dot product via a matrix product: 8 -Cross product: - 1 --2 - 1 -``` - -注意:点积只对三维vector有效。对于复数,Eigen的点积是第一个变量共轭和第二个变量的线性积。 - -## 基础的归约操作 - -Eigen提供了而一些归约函数:sum()、prod()、maxCoeff()和minCoeff(),他们对所有元素进行操作。 - -``` -#include -#include -using namespace std; -int main() -{ - Eigen::Matrix2d mat; - mat << 1, 2, - 3, 4; - cout << "Here is mat.sum(): " << mat.sum() << endl; - cout << "Here is mat.prod(): " << mat.prod() << endl; - cout << "Here is mat.mean(): " << mat.mean() << endl; - cout << "Here is mat.minCoeff(): " << mat.minCoeff() << endl; - cout << "Here is mat.maxCoeff(): " << mat.maxCoeff() << endl; - cout << "Here is mat.trace(): " << mat.trace() << endl; -} -``` - -输出 - -``` -Here is mat.sum(): 10 -Here is mat.prod(): 24 -Here is mat.mean(): 2.5 -Here is mat.minCoeff(): 1 -Here is mat.maxCoeff(): 4 -Here is mat.trace(): 5 -``` - -trace表示矩阵的迹,对角元素的和等价于 `a.diagonal().sum()` 。 - -minCoeff和maxCoeff函数也可以返回结果元素的位置信息。 - -``` -Matrix3f m = Matrix3f::Random(); - std::ptrdiff_t i, j; - float minOfM = m.minCoeff(&i,&j); - cout << "Here is the matrix m:\n" << m << endl; - cout << "Its minimum coefficient (" << minOfM - << ") is at position (" << i << "," << j << ")\n\n"; - RowVector4i v = RowVector4i::Random(); - int maxOfV = v.maxCoeff(&i); - cout << "Here is the vector v: " << v << endl; - cout << "Its maximum coefficient (" << maxOfV - << ") is at position " << i << endl; -``` - -输出 - -``` -Here is the matrix m: - 0.68 0.597 -0.33 --0.211 0.823 0.536 - 0.566 -0.605 -0.444 -Its minimum coefficient (-0.605) is at position (2,1) - -Here is the vector v: 1 0 3 -3 -Its maximum coefficient (3) is at position 2 -``` - -## 操作的有效性 - -Eigen会检测执行操作的有效性,在编译阶段Eigen会检测它们,错误信息是繁冗的,但错误信息会大写字母突出,比如: - -``` -Matrix3f m; -Vector4f v; -v = m*v; // Compile-time error: YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES -``` - -当然动态尺寸的错误要在运行时发现,如果在debug模式,assertions会触发后,程序将崩溃。 - -``` -MatrixXf m(3,3); -VectorXf v(4); -v = m * v; // Run-time assertion failure here: "invalid matrix product" -``` - -# Array类和元素级操作 - -## 为什么使用Array - -相对于Matrix提供的线性代数运算,Array类提供了更为一般的数组功能。Array类为元素级的操作提供了有效途径,比如点加(每个元素加值)或两个数据相应元素的点乘。 - -## Array - -Array是个类模板(类似于Matrx),前三个参数是必须指定的,后三个是可选的,这点和Matrix是相同的。 - -``` -Array -``` - -Eigen也提供的一些常用类定义,Array是同时支持一维和二维的(Matrix二维,Vector一维)。 - -| Type | Tyoedef | -| ----------------------------- | -------- | -| Array | ArrayXf | -| Array | Array3f | -| Array | ArrayXXd | -| Array | Array33d | - -## 获取元素 - -读写操作重载于matrix, `<<` 可以用于初始化array或打印。 - -``` -#include -#include -using namespace Eigen; -using namespace std; -int main() -{ - ArrayXXf m(2,2); - - // assign some values coefficient by coefficient - m(0,0) = 1.0; m(0,1) = 2.0; - m(1,0) = 3.0; m(1,1) = m(0,1) + m(1,0); - - // print values to standard output - cout << m << endl << endl; - - // using the comma-initializer is also allowed - m << 1.0,2.0, - 3.0,4.0; - - // print values to standard output - cout << m << endl; -} -``` - -## 加法和减法 - -和matrix类似,要求array的尺寸一致。同时支持`array+/-scalar`的操作! - -``` -#include -#include -using namespace Eigen; -using namespace std; -int main() -{ - ArrayXXf a(3,3); - ArrayXXf b(3,3); - a << 1,2,3, - 4,5,6, - 7,8,9; - b << 1,2,3, - 1,2,3, - 1,2,3; - - // Adding two arrays - cout << "a + b = " << endl << a + b << endl << endl; - // Subtracting a scalar from an array - cout << "a - 2 = " << endl << a - 2 << endl; -} -``` - -输出 - -``` -a + b = - 2 4 6 - 5 7 9 - 8 10 12 - -a - 2 = --1 0 1 - 2 3 4 - 5 6 7 -``` - -## 乘法 - -支持array*scalar(类似于matrix),但是当执行array*array时,执行的是相应元素的乘积,因此两个array必须具有相同的尺寸。 - -``` -int main() -{ - ArrayXXf a(2,2); - ArrayXXf b(2,2); - a << 1,2, - 3,4; - b << 5,6, - 7,8; - cout << "a * b = " << endl << a * b << endl; -} - -a * b = - 5 12 -21 32 -``` - -## 其他元素级操作 - -| Function | function | -| -------- | ------------------------- | -| abs | 绝对值 | -| sqrt | 平方根 | -| min(.) | 两个array相应元素的最小值 | - -``` -int main() -{ - ArrayXf a = ArrayXf::Random(5); - a *= 2; - cout << "a =" << endl - << a << endl; - cout << "a.abs() =" << endl - << a.abs() << endl; - cout << "a.abs().sqrt() =" << endl - << a.abs().sqrt() << endl; - cout << "a.min(a.abs().sqrt()) =" << endl - << a.min(a.abs().sqrt()) << endl; -} -``` - -## array和matrix之间的转换 - -当需要线性代数类操作时,请使用Matrix;但需要元素级操作时,需要使用Array。这样就需要提供两者的转化方法。 - -Matrix提供了.array()函数将它们转化为Array对象。 - -Array提供了.matrix()函数将它们转化为Matrix对象。 - -在Eigen,在表达式中混合Matrix和Array操作是被禁止的,但是可以将array表达式结果赋值为matrix。 - -另外,Matrix提供了cwiseProduct函数也实现了点乘。 - -``` -#include -#include -using namespace Eigen; -using namespace std; -int main() -{ - MatrixXf m(2,2); - MatrixXf n(2,2); - MatrixXf result(2,2); - m << 1,2, - 3,4; - n << 5,6, - 7,8; - result = m * n; - cout << "-- Matrix m*n: --" << endl << result << endl << endl; - result = m.array() * n.array(); - cout << "-- Array m*n: --" << endl << result << endl << endl; - result = m.cwiseProduct(n); - cout << "-- With cwiseProduct: --" << endl << result << endl << endl; - result = m.array() + 4; - cout << "-- Array m + 4: --" << endl << result << endl << endl; -} -``` - -输出 - -``` --- Matrix m*n: -- -19 22 -43 50 - --- Array m*n: -- - 5 12 -21 32 - --- With cwiseProduct: -- - 5 12 -21 32 - --- Array m + 4: -- -5 6 -7 8 -``` - -类似, `array1.matrix() * array2.matrix()` 将执行矩阵乘法。 - -# 块操作 - -块是matrix或array中的矩形子部分。 - -## 使用块 - -函数.block(),有两种形式 - -| operation | 构建一个动态尺寸的block | 构建一个固定尺寸的block | -| -------------------- | ----------------------- | ----------------------- | -| 起点(i,j)块大小(p,q) | .block(i,j,p,q) | .block< p,q >(i,j) | - -Eigen中,索引从0开始。 - -两个版本都可以用于固定尺寸和动态尺寸的matrix/array。功能是等价的,只是固定尺寸的版本在block较小时速度更快一些。 - -``` -int main() -{ - Eigen::MatrixXf m(4,4); - m << 1, 2, 3, 4, - 5, 6, 7, 8, - 9,10,11,12, - 13,14,15,16; - cout << "Block in the middle" << endl; - cout << m.block<2,2>(1,1) << endl << endl; - for (int i = 1; i <= 3; ++i) - { - cout << "Block of size " << i << "x" << i << endl; - cout << m.block(0,0,i,i) << endl << endl; - } -} -``` - -输出 - -``` -Block in the middle - 6 7 -10 11 - -Block of size 1x1 -1 - -Block of size 2x2 -1 2 -5 6 - -Block of size 3x3 - 1 2 3 - 5 6 7 - 9 10 11 -``` - -作为左值 - -``` -int main() -{ - Array22f m; - m << 1,2, - 3,4; - Array44f a = Array44f::Constant(0.6); - cout << "Here is the array a:" << endl << a << endl << endl; - a.block<2,2>(1,1) = m; - cout << "Here is now a with m copied into its central 2x2 block:" << endl << a << endl << endl; - a.block(0,0,2,3) = a.block(2,1,2,3); - cout << "Here is now a with bottom-right 2x3 block copied into top-left 2x2 block:" << endl << a << endl << endl; -} -``` - -输出 - -``` -Here is the array a: -0.6 0.6 0.6 0.6 -0.6 0.6 0.6 0.6 -0.6 0.6 0.6 0.6 -0.6 0.6 0.6 0.6 - -Here is now a with m copied into its central 2x2 block: -0.6 0.6 0.6 0.6 -0.6 1 2 0.6 -0.6 3 4 0.6 -0.6 0.6 0.6 0.6 - -Here is now a with bottom-right 2x3 block copied into top-left 2x2 block: - 3 4 0.6 0.6 -0.6 0.6 0.6 0.6 -0.6 3 4 0.6 -0.6 0.6 0.6 0.6 -``` - -## 行和列 - -| Operation | Method | -| --------- | ------------- | -| ith row | matrix.row(i) | -| jth colum | matrix.col(j) | - -``` -int main() -{ - Eigen::MatrixXf m(3,3); - m << 1,2,3, - 4,5,6, - 7,8,9; - cout << "Here is the matrix m:" << endl << m << endl; - cout << "2nd Row: " << m.row(1) << endl; - m.col(2) += 3 * m.col(0); - cout << "After adding 3 times the first column into the third column, the matrix m is:\n"; - cout << m << endl; -} -``` - -输出 - -``` -Here is the matrix m: -1 2 3 -4 5 6 -7 8 9 -2nd Row: 4 5 6 -After adding 3 times the first column into the third column, the matrix m is: - 1 2 6 - 4 5 18 - 7 8 30 -``` - -## 角相关操作 - -| operation | dynamic-size block | fixed-size block | -| ---------- | ------------------------------ | ---------------------------------- | -| 左上角p\*q | matrix.topLeftCorner(p,q); | matrix.topLeftCorner< p,q >(); | -| 左下角p\*q | matrix.bottomLeftCorner(p,q); | matrix.bottomLeftCorner< p,q >(); | -| 右上角p\*q | matrix.topRightCorner(p,q); | matrix.topRightCorner< p,q >(); | -| 右下角p\*q | matrix.bottomRightCorner(p,q); | matrix.bottomRightCorner< p,q >(); | -| 前q行 | matrix.topRows(q); | matrix.topRows< q >(); | -| 后q行 | matrix.bottomRows(q); | matrix.bottomRows< q >(); | -| 左p列 | matrix.leftCols(p); | matrix.leftCols< p >(); | -| 右p列 | matrix.rightCols(p); | matrix.rightCols< p >(); | - -``` -int main() -{ - Eigen::Matrix4f m; - m << 1, 2, 3, 4, - 5, 6, 7, 8, - 9, 10,11,12, - 13,14,15,16; - cout << "m.leftCols(2) =" << endl << m.leftCols(2) << endl << endl; - cout << "m.bottomRows<2>() =" << endl << m.bottomRows<2>() << endl << endl; - m.topLeftCorner(1,3) = m.bottomRightCorner(3,1).transpose(); - cout << "After assignment, m = " << endl << m << endl; -} -``` - -输出 - -``` -m.leftCols(2) = - 1 2 - 5 6 - 9 10 -13 14 - -m.bottomRows<2>() = - 9 10 11 12 -13 14 15 16 - -After assignment, m = - 8 12 16 4 - 5 6 7 8 - 9 10 11 12 -13 14 15 16 -``` - -## vectors的块操作 - -| operation | dynamic-size block | fixed-size block | -| -------------- | -------------------- | ----------------------- | -| 前n个 | vector.head(n); | vector.head< n >(); | -| 后n个 | vector.tail(n); | vector.tail< n >(); | -| i起始的n个元素 | vector.segment(i,n); | vector.segment< n >(i); | - -# 高级初始化方法 - -本篇介绍几种高级的矩阵初始化方法,重点介绍逗号初始化和特殊矩阵(单位阵、零阵)。 - -## 逗号初始化 - -Eigen提供了逗号操作符允许我们方便地为矩阵/向量/数组中的元素赋值。顺序是从左上到右下:自左到右,从上至下。对象的尺寸需要事先指定,初始化的参数也应该和要操作的元素数目一致。 - -``` -Matrix3f m; -m << 1, 2, 3, - 4, 5, 6, - 7, 8, 9; -std::cout << m; -``` - -初始化列表不仅可以是数值也可以是vectors或matrix。 - -``` -RowVectorXd vec1(3); -vec1 << 1, 2, 3; -std::cout << "vec1 = " << vec1 << std::endl; -RowVectorXd vec2(4); -vec2 << 1, 4, 9, 16; -std::cout << "vec2 = " << vec2 << std::endl; -RowVectorXd joined(7); -joined << vec1, vec2; -std::cout << "joined = " << joined << std::endl; -``` - -输出 - -``` -vec1 = 1 2 3 -vec2 = 1 4 9 16 -joined = 1 2 3 1 4 9 16 -``` - -也可以使用块结构。 - -``` -MatrixXf matA(2, 2); -matA << 1, 2, 3, 4; -MatrixXf matB(4, 4); -matB << matA, matA/10, matA/10, matA; -std::cout << matB << std::endl; -``` - -输出 - -``` - 1 2 0.1 0.2 - 3 4 0.3 0.4 -0.1 0.2 1 2 -0.3 0.4 3 4 -``` - -同时逗号初始化方式也可以用来为块表达式赋值。 - -``` -Matrix3f m; -m.row(0) << 1, 2, 3; -m.block(1,0,2,2) << 4, 5, 7, 8; -m.col(2).tail(2) << 6, 9; -std::cout << m; - -1 2 3 -4 5 6 -7 8 9 -``` - -## 特殊的矩阵和向量 - -零阵:类的静态成员函数Zero(),有三种定义形式。 - -``` -std::cout << "A fixed-size array:\n"; -Array33f a1 = Array33f::Zero(); -std::cout << a1 << "\n\n"; -std::cout << "A one-dimensional dynamic-size array:\n"; -ArrayXf a2 = ArrayXf::Zero(3); -std::cout << a2 << "\n\n"; -std::cout << "A two-dimensional dynamic-size array:\n"; -ArrayXXf a3 = ArrayXXf::Zero(3, 4); -std::cout << a3 << "\n"; -``` - -输出 - -``` -A fixed-size array: -0 0 0 -0 0 0 -0 0 0 - -A one-dimensional dynamic-size array: -0 -0 -0 - -A two-dimensional dynamic-size array: -0 0 0 0 -0 0 0 0 -0 0 0 0 -``` - -类似地,还有常量矩阵:Constant([rows],[cols],value),Random()随机矩阵。 - -单位阵Identity()方法只能使用与Matrix不使用Array,因为单位阵是个线性代数概念。 - -LinSpaced(size, low, high)可以从low到high等间距的size长度的序列,适用于vector和一维数组。 - -``` -ArrayXXf table(10, 4); -table.col(0) = ArrayXf::LinSpaced(10, 0, 90); -table.col(1) = M_PI / 180 * table.col(0); -table.col(2) = table.col(1).sin(); -table.col(3) = table.col(1).cos(); -std::cout << " Degrees Radians Sine Cosine\n"; -std::cout << table << std::endl; -``` - -输出 - -``` - Degrees Radians Sine Cosine - 0 0 0 1 - 10 0.175 0.174 0.985 - 20 0.349 0.342 0.94 - 30 0.524 0.5 0.866 - 40 0.698 0.643 0.766 - 50 0.873 0.766 0.643 - 60 1.05 0.866 0.5 - 70 1.22 0.94 0.342 - 80 1.4 0.985 0.174 - 90 1.57 1 -4.37e-08 -``` - -### 功能函数 - -Eigen也提供可同样功能的函数:setZero(), MatrixBase::setIdentity()和 DenseBase::setLinSpaced()。 - -``` -const int size = 6; -MatrixXd mat1(size, size); -mat1.topLeftCorner(size/2, size/2) = MatrixXd::Zero(size/2, size/2); -mat1.topRightCorner(size/2, size/2) = MatrixXd::Identity(size/2, size/2); -mat1.bottomLeftCorner(size/2, size/2) = MatrixXd::Identity(size/2, size/2); -mat1.bottomRightCorner(size/2, size/2) = MatrixXd::Zero(size/2, size/2); -std::cout << mat1 << std::endl << std::endl; -MatrixXd mat2(size, size); -mat2.topLeftCorner(size/2, size/2).setZero(); -mat2.topRightCorner(size/2, size/2).setIdentity(); -mat2.bottomLeftCorner(size/2, size/2).setIdentity(); -mat2.bottomRightCorner(size/2, size/2).setZero(); -std::cout << mat2 << std::endl << std::endl; -MatrixXd mat3(size, size); -mat3 << MatrixXd::Zero(size/2, size/2), MatrixXd::Identity(size/2, size/2), - MatrixXd::Identity(size/2, size/2), MatrixXd::Zero(size/2, size/2); -std::cout << mat3 << std::endl; -``` - -输出均为 - -``` -0 0 0 1 0 0 -0 0 0 0 1 0 -0 0 0 0 0 1 -1 0 0 0 0 0 -0 1 0 0 0 0 -0 0 1 0 0 0 -``` - -三种赋值(初始化)的方式逗号初始化、特殊阵的静态方法和功能函数setXxx()。 - -## 表达式变量 - -上面的静态方法如 Zero()、Constant()并不是直接返回一个矩阵或数组,实际上它们返回的是是‘expression object’,只是临时被使用/被用于优化。 - -``` -m = (m + MatrixXd::Constant(3,3,1.2)) * 50; -``` - -`MatrixXf::Constant(3,3,1.2)`构建的是一个3*3的矩阵表达式(临时变量)。 - -逗号初始化的方式也可以构建这种临时变量,这是为了获取真正的矩阵需要调用finished()函数: - -``` -MatrixXf mat = MatrixXf::Random(2, 3); -std::cout << mat << std::endl << std::endl; -mat = (MatrixXf(2,2) << 0, 1, 1, 0).finished() * mat; -std::cout << mat << std::endl; -``` - -输出 - -``` - 0.68 0.566 0.823 --0.211 0.597 -0.605 - --0.211 0.597 -0.605 - 0.68 0.566 0.823 -``` - -# 归约、迭代器和广播 - -## 归约 - -在Eigen中,有些函数可以统计matrix/array的某类特征,返回一个标量。 - -``` -int main() -{ - Eigen::Matrix2d mat; - mat << 1, 2, - 3, 4; - cout << "Here is mat.sum(): " << mat.sum() << endl; - cout << "Here is mat.prod(): " << mat.prod() << endl; - cout << "Here is mat.mean(): " << mat.mean() << endl; - cout << "Here is mat.minCoeff(): " << mat.minCoeff() << endl; - cout << "Here is mat.maxCoeff(): " << mat.maxCoeff() << endl; - cout << "Here is mat.trace(): " << mat.trace() << endl; -} -``` - -### 范数计算 - -L2范数 squareNorm(),等价于计算vector的自身点积,norm()返回squareNorm的开方根。 - -这些操作应用于matrix,norm() 会返回Frobenius或Hilbert-Schmidt范数。 - -如果你想使用其他Lp范数,可以使用lpNorm< p >()方法。p可以取Infinity,表示L∞范数。 - -``` -int main() -{ - VectorXf v(2); - MatrixXf m(2,2), n(2,2); - - v << -1, - 2; - - m << 1,-2, - -3,4; - cout << "v.squaredNorm() = " << v.squaredNorm() << endl; - cout << "v.norm() = " << v.norm() << endl; - cout << "v.lpNorm<1>() = " << v.lpNorm<1>() << endl; - cout << "v.lpNorm() = " << v.lpNorm() << endl; - cout << endl; - cout << "m.squaredNorm() = " << m.squaredNorm() << endl; - cout << "m.norm() = " << m.norm() << endl; - cout << "m.lpNorm<1>() = " << m.lpNorm<1>() << endl; - cout << "m.lpNorm() = " << m.lpNorm() << endl; -} -``` - -输出 - -``` -v.squaredNorm() = 5 -v.norm() = 2.23607 -v.lpNorm<1>() = 3 -v.lpNorm() = 2 - -m.squaredNorm() = 30 -m.norm() = 5.47723 -m.lpNorm<1>() = 10 -m.lpNorm() = 4 -``` - -**Operator norm**: 1-norm和∞-norm可以通过其他方式得到。 - -``` -int main() -{ - MatrixXf m(2,2); - m << 1,-2, - -3,4; - cout << "1-norm(m) = " << m.cwiseAbs().colwise().sum().maxCoeff() - << " == " << m.colwise().lpNorm<1>().maxCoeff() << endl; - cout << "infty-norm(m) = " << m.cwiseAbs().rowwise().sum().maxCoeff() - << " == " << m.rowwise().lpNorm<1>().maxCoeff() << endl; -} - -1-norm(m) = 6 == 6 -infty-norm(m) = 7 == 7 -``` - -### 布尔归约 - -all()=true matrix/array中的所有算术是true any()=true matrix/array中至少有一个元素是true count() 返回为true元素的数目 - -``` -#include -#include -using namespace std; -using namespace Eigen; -int main() -{ - ArrayXXf a(2,2); - - a << 1,2, - 3,4; - cout << "(a > 0).all() = " << (a > 0).all() << endl; - cout << "(a > 0).any() = " << (a > 0).any() << endl; - cout << "(a > 0).count() = " << (a > 0).count() << endl; - cout << endl; - cout << "(a > 2).all() = " << (a > 2).all() << endl; - cout << "(a > 2).any() = " << (a > 2).any() << endl; - cout << "(a > 2).count() = " << (a > 2).count() << endl; -} -``` - -输出 - -``` -(a > 0).all() = 1 -(a > 0).any() = 1 -(a > 0).count() = 4 - -(a > 2).all() = 0 -(a > 2).any() = 1 -(a > 2).count() = 2 -``` - -## 迭代器(遍历) - -当我们想获取某元素在Matrix或Array中的位置的时候,迭代器是必须的。常用的有:minCoeff和maxCoeff。 - -``` -int main() -{ - Eigen::MatrixXf m(2,2); - - m << 1, 2, - 3, 4; - //get location of maximum - MatrixXf::Index maxRow, maxCol; - float max = m.maxCoeff(&maxRow, &maxCol); - //get location of minimum - MatrixXf::Index minRow, minCol; - float min = m.minCoeff(&minRow, &minCol); - cout << "Max: " << max << ", at: " << - maxRow << "," << maxCol << endl; - cout << "Min: " << min << ", at: " << - minRow << "," << minCol << endl; -} - -Max: 4, at: 1,1 -Min: 1, at: 0,0 -``` - -## 部分归约 - -Eigen中支持对Matrx或Array的行/行进行归约操作。部分归约可以使用colwise()/rowwise()函数。 - -``` -int main() -{ - Eigen::MatrixXf mat(2,4); - mat << 1, 2, 6, 9, - 3, 1, 7, 2; - - std::cout << "Column's maximum: " << std::endl - << mat.colwise().maxCoeff() << std::endl; -} - -Column's maximum: -3 2 7 9 -``` - -类似,针对行也可以,只是返回的是列向量而已。 - -``` -int main() -{ - Eigen::MatrixXf mat(2,4); - mat << 1, 2, 6, 9, - 3, 1, 7, 2; - - std::cout << "Row's maximum: " << std::endl - << mat.rowwise().maxCoeff() << std::endl; -} - -Row's maximum: -9 -7 -``` - -## 结合部分归约和其他操作 - -例子:寻找和最大的列向量。 - -``` -int main() -{ - MatrixXf mat(2,4); - mat << 1, 2, 6, 9, - 3, 1, 7, 2; - - MatrixXf::Index maxIndex; - float maxNorm = mat.colwise().sum().maxCoeff(&maxIndex); - - std::cout << "Maximum sum at position " << maxIndex << std::endl; - std::cout << "The corresponding vector is: " << std::endl; - std::cout << mat.col( maxIndex ) << std::endl; - std::cout << "And its sum is is: " << maxNorm << std::endl; -} -``` - -输出 - -``` -Maximum sum at position 2 -The corresponding vector is: -6 -7 -And its sum is is: 13 -``` - -## 广播 - -广播是针对vector的,将vector沿行/列重复构建一个matrix,便于后期运算。 - -``` -int main() -{ - Eigen::MatrixXf mat(2,4); - Eigen::VectorXf v(2); - - mat << 1, 2, 6, 9, - 3, 1, 7, 2; - - v << 0, - 1; - - //add v to each column of m - mat.colwise() += v; - - std::cout << "Broadcasting result: " << std::endl; - std::cout << mat << std::endl; -} -``` - -输出 - -``` -Broadcasting result: -1 2 6 9 -4 2 8 3 -``` - -注意:对Array类型,*=,/=和/这些操作可以进行行/列级的操作,但不使用与Matrix,因为会与矩阵乘混淆。 - -## 结合广播和其他操作 - -示例:计算矩阵中哪列与目标向量距离最近。 - -``` -int main() -{ - Eigen::MatrixXf m(2,4); - Eigen::VectorXf v(2); - - m << 1, 23, 6, 9, - 3, 11, 7, 2; - - v << 2, - 3; - MatrixXf::Index index; - // find nearest neighbour - (m.colwise() - v).colwise().squaredNorm().minCoeff(&index); - cout << "Nearest neighbour is column " << index << ":" << endl; - cout << m.col(index) << endl; -} -``` - -输出 - -``` -Nearest neighbour is column 0: -1 -3 -``` - -# 原生缓存的接口:Map类 - -这篇将解释Eigen如何与原生raw C/C++ 数组混合编程。 - -## 简介 - -Eigen中定义了一系列的vector和matrix,相比copy数据,更一般的方式是复用数据的内存,将它们转变为Eigen类型。Map类很好地实现了这个功能。 - -## Map类型 - -Map的定义 - -``` -Map > -``` - -默认情况下,Mao只需要一个模板参数。 - -为了构建Map变量,我们需要其余的两个信息:一个指向元素数组的指针,Matrix/vector的尺寸。定义一个float类型的矩阵: `Map mf(pf,rows,columns);` pf是一个数组指针float *。 - -固定尺寸的整形vector声明: `Map mi(pi);` - -注意:Map没有默认的构造函数,你需要传递一个指针来初始化对象。 - -Mat是灵活地足够去容纳多种不同的数据表示,其他的两个模板参数: - -``` -Map -``` - -MapOptions标识指针是否是对齐的(Aligned),默认是Unaligned。 - -StrideType表示内存数组的组织方式:行列的步长。 - -``` -int array[8]; -for(int i = 0; i < 8; ++i) array[i] = i; -cout << "Column-major:\n" << Map >(array) << endl; -cout << "Row-major:\n" << Map >(array) << endl; -cout << "Row-major using stride:\n" << - Map, Unaligned, Stride<1,4> >(array) << endl; -``` - -输出 - -``` -Column-major: -0 2 4 6 -1 3 5 7 -Row-major: -0 1 2 3 -4 5 6 7 -Row-major using stride: -0 1 2 3 -4 5 6 7 -``` - -## 使用Map变量 - -可以像Eigen的其他类型一样来使用Map类型。 - -``` -typedef Matrix MatrixType; -typedef Map MapType; -typedef Map MapTypeConst; // a read-only map -const int n_dims = 5; - -MatrixType m1(n_dims), m2(n_dims); -m1.setRandom(); -m2.setRandom(); -float *p = &m2(0); // get the address storing the data for m2 -MapType m2map(p,m2.size()); // m2map shares data with m2 -MapTypeConst m2mapconst(p,m2.size()); // a read-only accessor for m2 -cout << "m1: " << m1 << endl; -cout << "m2: " << m2 << endl; -cout << "Squared euclidean distance: " << (m1-m2).squaredNorm() << endl; -cout << "Squared euclidean distance, using map: " << - (m1-m2map).squaredNorm() << endl; -m2map(3) = 7; // this will change m2, since they share the same array -cout << "Updated m2: " << m2 << endl; -cout << "m2 coefficient 2, constant accessor: " << m2mapconst(2) << endl; -/* m2mapconst(2) = 5; */ // this yields a compile-time error -``` - -输出 - -``` -m1: 0.68 -0.211 0.566 0.597 0.823 -m2: -0.605 -0.33 0.536 -0.444 0.108 -Squared euclidean distance: 3.26 -Squared euclidean distance, using map: 3.26 -Updated m2: -0.605 -0.33 0.536 7 0.108 -m2 coefficient 2, constant accessor: 0.536 -``` - -Eigen提供的函数都兼容Map对象。 - -## 改变mapped数组 - -Map对象声明后,可以通过C++的placement new语法来改变Map的数组。 - -``` -int data[] = {1,2,3,4,5,6,7,8,9}; -Map v(data,4); -cout << "The mapped vector v is: " << v << "\n"; -new (&v) Map(data+4,5); -cout << "Now v is: " << v << "\n"; - -The mapped vector v is: 1 2 3 4 -Now v is: 5 6 7 8 9 -``` - -Eigen并没有为matrix提供直接的Reshape和Slicing的API,但是这些特性可以通过Map类来实现。 - -## Reshape - -reshape操作是改变matrix的尺寸大小但保持元素不变。采用的方法是创建一个不同“视图” Map。 - -``` -MatrixXf M1(3,3); // Column-major storage -M1 << 1, 2, 3, - 4, 5, 6, - 7, 8, 9; -Map v1(M1.data(), M1.size()); -cout << "v1:" << endl << v1 << endl; -Matrix M2(M1); -Map v2(M2.data(), M2.size()); -cout << "v2:" << endl << v2 << endl; -``` - -输出 - -``` -v1: -1 4 7 2 5 8 3 6 9 -v2: -1 2 3 4 5 6 7 8 9 -``` - -reshape 2*6的矩阵到 6*2 - -``` -MatrixXf M1(2,6); // Column-major storage -M1 << 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12; -Map M2(M1.data(), 6,2); -cout << "M2:" << endl << M2 << endl; -``` - -输出 - -``` -M2: - 1 4 - 7 10 - 2 5 - 8 11 - 3 6 - 9 12 -``` - -## Slicing - -也是通过Map实现的,比如:每p个元素获取一个。 - -``` -RowVectorXf v = RowVectorXf::LinSpaced(20,0,19); -cout << "Input:" << endl << v << endl; -Map > v2(v.data(), v.size()/2); -cout << "Even:" << v2 << endl; -``` - -输出 - -``` -Input: - 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 -Even: 0 2 4 6 8 10 12 14 16 18 -``` - -## 混淆 - -在Eigen中,当变量同时出现在左值和右值,赋值操作可能会带来混淆问题。这一篇将解释什么是混淆,什么时候是有害的,怎么使用做。 - -## 例子 - -``` -MatrixXi mat(3,3); -mat << 1, 2, 3, 4, 5, 6, 7, 8, 9; -cout << "Here is the matrix mat:\n" << mat << endl; -// This assignment shows the aliasing problem -mat.bottomRightCorner(2,2) = mat.topLeftCorner(2,2); -cout << "After the assignment, mat = \n" << mat << endl; -``` - -输出 - -``` -Here is the matrix mat: -1 2 3 -4 5 6 -7 8 9 -After the assignment, mat = -1 2 3 -4 1 2 -7 4 1 -``` - -在 `mat.bottomRightCorner(2,2) = mat.topLeftCorner(2,2);` 赋值中展示了混淆。 - -mat(1,1) 在bottomRightCorner(2,2)和topLeftCorner(2,2)都存在。赋值结果中mat(2,2)本应该赋予操作前mat(1,1)的值=5。但是,最终程序结果mat(2,2)=1。原因是Eigen使用了lazy evaluation(懒惰评估),上面等价于 - -``` -mat(1,1) = mat(0,0); -mat(1,2) = mat(0,1); -mat(2,1) = mat(1,0); -mat(2,2) = mat(1,1); -``` - -下面会解释如何通过eval()来解决这个问题。 - -混淆还会在缩小矩阵时出现,比如 `vec = vec.head(n)` 和 `mat = mat.block(i,j,r,c)`。 - -一般来说,混淆在编译阶段很难被检测到。比如第一个例子,如果mat再大一些可能就不会出现混淆了。但是Eigen可以在运行时检测某些混淆,如前面讲的例子。 - -``` -Matrix2i a; a << 1, 2, 3, 4; -cout << "Here is the matrix a:\n" << a << endl; -a = a.transpose(); // !!! do NOT do this !!! -cout << "and the result of the aliasing effect:\n" << a << endl; -Here is the matrix a: -1 2 -3 4 -and the result of the aliasing effect: -1 2 -2 4 -``` - -我们可以通过EIGEN_NO_DEBUG宏,在编译时关闭运行时的断言。 - -## 解决混淆问题 - -Eigen需要把右值赋值为一个临时matrix/array,然后再将临时值赋值给左值,便可以解决混淆。eval()函数实现了这个功能。 - -``` -MatrixXi mat(3,3); -mat << 1, 2, 3, 4, 5, 6, 7, 8, 9; -cout << "Here is the matrix mat:\n" << mat << endl; -// The eval() solves the aliasing problem -mat.bottomRightCorner(2,2) = mat.topLeftCorner(2,2).eval(); -cout << "After the assignment, mat = \n" << mat << endl; -``` - -输出 - -``` -Here is the matrix mat: -1 2 3 -4 5 6 -7 8 9 -After the assignment, mat = -1 2 3 -4 1 2 -7 4 5 -``` - -同样: `a = a.transpose().eval();` ,当然我们最好使用 transposeInPlace()。如果存在xxxInPlace函数,推荐使用这类函数,它们更加清晰地标明了你在做什么。提供的这类函数: - -| Origin | In-place | -| ----------------------- | ------------------------------ | -| MatrixBase::adjoint() | MatrixBase::adjointInPlace() | -| DenseBase::reverse() | DenseBase::reverseInPlace() | -| LDLT::solve() | LDLT::solveInPlace() | -| LLT::solve() | LLT::solveInPlace() | -| TriangularView::solve() | TriangularView::solveInPlace() | -| DenseBase::transpose() | DenseBase::transposeInPlace() | - -而针对`vec = vec.head(n)`这种情况,推荐使用`conservativeResize()`。 - -## 混淆和component级的操作。 - -组件级是指整体的操作,比如matrix加法、scalar乘、array乘等,这类操作是安全的,不会出现混淆。 - -``` -MatrixXf mat(2,2); -mat << 1, 2, 4, 7; -cout << "Here is the matrix mat:\n" << mat << endl << endl; -mat = 2 * mat; -cout << "After 'mat = 2 * mat', mat = \n" << mat << endl << endl; -mat = mat - MatrixXf::Identity(2,2); -cout << "After the subtraction, it becomes\n" << mat << endl << endl; -ArrayXXf arr = mat; -arr = arr.square(); -cout << "After squaring, it becomes\n" << arr << endl << endl; -``` - -输出 - -``` -Here is the matrix mat: -1 2 -4 7 - -After 'mat = 2 * mat', mat = - 2 4 - 8 14 - -After the subtraction, it becomes - 1 4 - 8 13 - -After squaring, it becomes - 1 16 - 64 169 -``` - -## 混淆和矩阵的乘法 - -在Eigen中,矩阵的乘法一般都会出现混淆。除非是方阵(实质是元素级的乘)。 - -``` -MatrixXf matA(2,2); -matA << 2, 0, 0, 2; -matA = matA * matA; -cout << matA; - -4 0 -0 4 -``` - -其他的操作,Eigen默认都是存在混淆的。所以Eigen对矩阵乘法自动引入了临时变量,对的`matA=matA*matA`这是必须的,但是对`matB=matA*matA`这样便是不必要的了。我们可以使用noalias()函数来声明这里没有混淆,matA*matA的结果可以直接赋值为matB。 - -``` -matB.noalias() = matA * matA; -``` - -从Eigen3.3开始,如果目标矩阵resize且结果不直接赋值给目标矩阵,默认不存在混淆。 - -``` -MatrixXf A(2,2), B(3,2); -B << 2, 0, 0, 3, 1, 1; -A << 2, 0, 0, -2; -A = (B * A).cwiseAbs();//cwiseAbs()不直接赋给目标 -//A = (B * A).eval().cwiseAbs() -cout << A; -``` - -当然,对于任何混淆问题,都可以通过`matA=(matB*matA).eval()` 来解决。 - -## 总结 - -当相同的矩阵或array在等式左右都出现时,很容易出现混淆。 - -1. compnent级别的操作不用考虑混淆。 -2. 矩阵相乘,Eigen默认会解决混淆问题,如果你确定不会出现混淆,可以使用noalias()来提效。 -3. 混淆出现时,可以用eval()和xxxInPlace()函数解决。 - -## 存储顺序 - -对于矩阵和二维数组有两种存储方式,列优先和行优先。 - -假设矩阵: - -![img](https://images2015.cnblogs.com/blog/532915/201701/532915-20170125204938597-130252060.png) - -按行优先存储,内存中形式如下: - -``` -8 2 2 9 9 1 4 4 3 5 4 5 -``` - -列优先,内存格式: - -``` -8 9 3 2 1 5 2 4 4 9 4 5 -Matrix Acolmajor; -Acolmajor << 8, 2, 2, 9, - 9, 1, 4, 4, - 3, 5, 4, 5; -cout << "The matrix A:" << endl; -cout << Acolmajor << endl << endl; -cout << "In memory (column-major):" << endl; -for (int i = 0; i < Acolmajor.size(); i++) - cout << *(Acolmajor.data() + i) << " "; -cout << endl << endl; -Matrix Arowmajor = Acolmajor; -cout << "In memory (row-major):" << endl; -for (int i = 0; i < Arowmajor.size(); i++) - cout << *(Arowmajor.data() + i) << " "; -cout << endl; -``` - -输出 - -``` -The matrix A: -8 2 2 9 -9 1 4 4 -3 5 4 5 - -In memory (column-major): -8 9 3 2 1 5 2 4 4 9 4 5 - -In memory (row-major): -8 2 2 9 9 1 4 4 3 5 4 5 -``` - -PlainObjectBase::data()函数可以返回矩阵中第一个元素的内存位置。 - -## 存储顺序及选择 - -Matrix类模板中可以设定存储的方向,RowMajor表示行优先,ColMajor表示列优先。默认是列优先。 - -如何选择存储方式呢? - -1. 如果要和其他库合作开发,为了转化方便,可以选择同样的存储方式。 -2. 应用中涉及大量行遍历操作,应该选择行优先,寻址更快。反之亦然。 -3. 默认是列优先,而且大多库都是按照这个顺序的,默认的不失为较好的。 - -## 总结 - -本来想春节前任务比较少,翻译完所有的Eigen系列的。但是我的目的是为了使用google的非线性优化库[ceres](http://ceres-solver.org/installation.html#getting-the-source-code),介绍了这些基本知识也够用了,如果遇到不清楚的函数可以直接到Eigen的官网查询。 - -## Reference - -* https://www.cnblogs.com/houkai/p/6347408.html -* https://www.cnblogs.com/houkai/p/6347648.html -* https://www.cnblogs.com/houkai/p/6348044.html -* https://www.cnblogs.com/houkai/p/6349970.html -* https://www.cnblogs.com/houkai/p/6349974.html -* https://www.cnblogs.com/houkai/p/6351358.html -* https://www.cnblogs.com/houkai/p/6351609.html -* https://www.cnblogs.com/houkai/p/6349981.html -* https://www.cnblogs.com/houkai/p/6349988.html -* https://www.cnblogs.com/houkai/p/6349990.html -* https://www.cnblogs.com/houkai/p/6349991.html -* [**Eigen Cheat sheet**](https://gist.github.com/gocarlos/c91237b02c120c6319612e42fa196d77#file-eigen-cheat-sheet) - diff --git a/doc/src/linear_algebra.md b/doc/src/linear_algebra.md deleted file mode 100644 index 634d2010..00000000 --- a/doc/src/linear_algebra.md +++ /dev/null @@ -1,14 +0,0 @@ -# 线性代数 - -* https://www.3blue1brown.com/ - -* https://www.zhihu.com/column/c_1068883024023834624 - -### 矩阵分解 - -* [LU分解](https://zhuanlan.zhihu.com/p/54943042) -* [QR分解](https://zhuanlan.zhihu.com/p/54957185) -* SVD分解 -* PCA分解 -* NMF分解 - diff --git a/doc/src/praat_textgrid.md b/doc/src/praat_textgrid.md deleted file mode 100644 index 06c4f879..00000000 --- a/doc/src/praat_textgrid.md +++ /dev/null @@ -1,258 +0,0 @@ -# Praat and TextGrid - -* [**Praat: doing phonetics by computer**](https://www.fon.hum.uva.nl/praat/) -* [TextGrid](https://github.com/kylebgorman/textgrid) - -## Praat - -**Praat语音学软件**,原名**Praat: doing phonetics by computer**,通常简称**Praat**,是一款[跨平台](https://zh.wikipedia.org/wiki/跨平台)的多功能[语音学](https://zh.wikipedia.org/wiki/语音学)专业[软件](https://zh.wikipedia.org/wiki/软件),主要用于对[数字化](https://zh.wikipedia.org/wiki/数字化)的[语音](https://zh.wikipedia.org/wiki/语音)[信号](https://zh.wikipedia.org/wiki/信号)进行[分析](https://zh.wikipedia.org/w/index.php?title=语音分析&action=edit&redlink=1)、标注、[处理](https://zh.wikipedia.org/wiki/数字信号处理)及[合成](https://zh.wikipedia.org/wiki/语音合成)等实验,同时生成各种[语图](https://zh.wikipedia.org/w/index.php?title=语图&action=edit&redlink=1)和文字报表。 - - - - - - - -## TextGrid - -### TextGrid文件结构 - -```text -第一行是固定的:File type = "ooTextFile" -第二行也是固定的:Object class = "TextGrid" -空一行 -xmin = xxxx.xxxx  # 表示开始时间 -xmax = xxxx.xxxx  # 表示结束时间 -tiers?  # 这一行固定 -size = 4  # 表示这个文件有几个item, item也叫tiers, 可以翻译为'层', 这个值是几,就表示有几个item -item []: -    item [1]: -        class = "IntervalTier" -        name = "phone" -        xmin = 1358.8925 -        xmax = 1422.5525 -        intervals: size = 104 -        intervals [1]: -            xmin = 1358.8925 -            xmax = 1361.8925 -            text = "sil" -        intervals [2]: -            xmin = 1361.8925 -            xmax = 1362.0125 -            text = "R" -        intervals [3]: -            ... -        intervals [104]: -            xmin = 1422.2325 -            xmax = 1422.5525 -            text = "sil" -    item [2]: -        class = "IntervalTier" -        name = "word" -        xmin = 1358.8925 -        xmax = 1422.5525 -        intervals: size = 3 -        intervals [1]: -            xmin = 1358.8925 -            xmax = 1361.8925 -            text = "sp" -``` - -textgrid 文件中的 size 的值是几就表示有几个 item, 每个 item 下面包含 class, name, xmin, xmax, intervals 的键值对,item 中的 intervals: size 是几就表示这个 item 中有几个 intervals,每个 intervals 有 xmin, xmax, text 三个键值参数。所有 item 中的 xmax - xmin 的值是一样的。 - -### 安装 - -```python -pip3 install textgrid -``` - -### 使用 - -1. 读一个textgrid文件 - - ```python - import textgrid - tg = textgrid.TextGrid() - tg.read('file.TextGrid') # 'file.TextGrid' 是文件名 - ``` - - tg.tiers属性: - 会把文件中的所有item打印出来, print(tg.tiers) 的结果如下: - - ```text - [IntervalTier( - phone, [ - Interval(1358.89250, 1361.89250, sil), - Interval(1361.89250, 1362.01250, R), - Interval(1362.01250, 1362.13250, AY1), - Interval(1362.13250, 1362.16250, T), - - ... - ] - ) - ] - ``` - - 此外, tg.tiers[0] 表示第一个 IntervalTier, 支持继续用中括号取序列, '.'来取属性. - 比如: - - ```text - tg.tiers[0][0].mark --> 'sil' - tg.tiers[0].name --> 'phone' - tg.tiers[0][0].minTime --> 1358.8925 - tg.tiers[0].intervals --> [Interval(1358.89250, 1361.89250, sil), ..., Interval(1422.23250, 1422.55250, sil)] - tg.tiers[0].maxTime --> 1422.55250 - ``` - - TextGrid 模块中包含四种对象 - - ``` - PointTier 可以理解为标记(点)的集合 - IntervalTier 可以理解为时长(区间)的集合 - Point 可以理解为标记 - Interval 可以理解为时长 - ``` - - - -2. textgrid库中的对象 - **IntervalTier** 对象: - 方法 - - ``` - add(minTime, maxTime, mark): 添加一个标记,需要同时传入起止时间, 和mark的名字. - addInterval(interval): 添加一个Interval对象, 该Interval对象中已经封装了起止时间. - remove(minTime, maxTime, mark): 删除一个Interval - removeInterval(interval): 删除一个Interval - indexContaining(time): 传入时间或Point对象, 返回包含该时间的Interval对象的下标 - 例如: - print(tg[0].indexContaining(1362)) --> 1 - 表示tg[0] 中包含1362时间点的是 下标为1的 Interval 对象 - intervalContaining(): 传入时间或Point对象, 返回包含该时间的Interval对象 - 例如 - print(tg[0].intervalContaining(1362)) --> Interval(1361.89250, 1362.01250, R) - read(f): f是文件对象, 读一个TextGrid文件 - write(f): f是文件对象, 写一个TextGrid文件 - fromFile(f_path): f_path是文件路径, 从一个文件读 - bounds(): 返回一个元组, (minTime, maxTime) - ``` - - - 属性 - - ``` - intervals --> 返回所有的 interval 的列表 - maxTime --> 返回 number(decimal.Decimal)类型, 表示结束时间 - minTime --> 返回 number(decimal.Decimal)类型, 表示开始时间 - name --> 返回字符串 - strict -- > 返回bool值, 表示是否严格TextGrid格式 - ``` - - ​ - - **PointTier** 对象: - 方法 - - ``` - add(minTime, maxTime, mark): 添加一个标记,需要同时传入起止时间, 和mark的名字. - addPoint(point): 添加一个Point对象, 该Point对象中已经封装了起止时间. - remove(time, mark): 删除一个 point, 传入时间和mark - removePoint(point): 删除一个 point, 传入point对象 - read(f): 读, f是文件对象 - write(f): 写, f是文件对象 - fromFile(f_path): f_path是文件路径, 从一个文件读 - bounds(): 返回一个元组, (minTime, maxTime) - ``` - - - 属性 - - ``` - points 返回所有的 point 的列表 - maxTime 和IntervalTier一样, 返回结束时间 - minTime 和IntervalTier一样, 返回开始时间 - name 返回name - ``` - - - - **Point** 对象: - 支持比较大小, 支持加减运算 - 属性: - - ``` - mark: - time: - ``` - - ​ - - **Interval** 对象: - 支持比较大小, 支持加减运算 - 支持 in, not in 的运算 - 方法: - - ``` - duration(): 返回number 类型, 表示这个Interval的持续时间 - bounds(): --> 返回元组, (minTime, maxTime) - overlaps(Interval): --> 返回bool值, 判断本Interval的时间和传入的的Interval的时间是否重叠, 是返回True - ``` - - 属性: - - ``` - mark - maxTime - minTime - strick: --> 返回bool值, 判断格式是否严格的TextGrid格式 - ``` - - **TextGrid** 对象: - 支持列表的取值,复制, 迭代, 求长度, append, extend, pop方法 - 方法: - - ``` - getFirst(tierName) 返回第一个名字为tierName的tier - getList(tierName) 返回名字为tierName的tier的列表 - getNames() 返回所有tier的名字的列表 - append(tier) 添加一个tier作为其中的元素 - extend(tiers) 添加多个tier作为其中的元素 - pop(tier) 删除一个tier - read(f) f是文件对象 - write(f) f是文件对象 - fromFile(f_path) f_path是文件路径 - ``` - - 属性: - - ``` - maxTime - minTime - name - strict - tiers 返回所有tiers的列表 - ``` - - **MLF** 对象 - MLF('xxx.mlf') - 'xxx.mlf'为mlf格式的文件, - 读取hvite-o sm生成的htk.mlf文件并将其转换为 TextGrid的列表 - 方法: - - ``` - read(f) f是文件对象 - write(prefix='') prefix是写出路径的前缀,可选 - ``` - - 属性: - - ``` - grids: --> 返回读取的grids的列表 - ``` - - - -## Reference - -* https://zh.wikipedia.org/wiki/Praat%E8%AF%AD%E9%9F%B3%E5%AD%A6%E8%BD%AF%E4%BB%B6 -* https://blog.csdn.net/duxin_csdn/article/details/88966295 diff --git a/doc/src/speech_synthesis.md b/doc/src/speech_synthesis.md deleted file mode 100644 index dc36b911..00000000 --- a/doc/src/speech_synthesis.md +++ /dev/null @@ -1,145 +0,0 @@ -# Speech Synthesis - -* [爱丁堡大学公开课](http://speech.zone/courses/speech-synthesis) - -* ### 推荐书籍 - - 1. Daniel Jurafsky and James H. Martin, Speech and language processing: An introduction to natural language processing, computational linguistics, and speech recognition. 这本书之前在学习语音识别的时候也经常翻阅。 推荐阅读章节: Ch 7 & Ch 8 (都读过啦~) - 2. Xuedong Huang, Alex Aceoro, Hsiao-Wuen Hon, Spoken Language Processing: A guide to theory, algorithm, and system development, Prentice Hall, 2011 这本书的三位作者都是大佬,本书推荐阅读 Ch2, Ch5, Ch6, Part IV: Text-to-Speech Systems. 学习一下基础知识点,如信号处理等 - 3. Paul Taylor, Text-to-Speech Synthesis, Cambridege University Press, 2009. 比较系统地讲述了神经网络之前的语音合成系统。 - - - -### 语音合成 - -现代语音合成主要包含文本分析和语音合成 - - - -#### 文本分析 - -文本分析主要分为 - -- **断句** : 怎么判断一句句子结束了,单纯用句号来切分并不靠谱,比如 ‘B.C.’, ‘Dr.J.M.’,’。。。’ -- **文本归一化** : 根据上下文消除一些词的读法,常见有数字的读法,”In 1950, he went to” -> “nineteen fifty”, “There are 1950 sheep.” => “one thousand and fifty”, “The code number is 1950” -> “one nine five zero”. -- **分词** : 将句子分成一个个的词,对于中文这种没有空格作为天然分隔符的语言是需要分词单元的。 -- **词性分析** : 将分好的词中的每个词进行标注,”动词,名词,形容词,…” -- **注音** : 有些词的读音在不同上下文中发音是不一样的,比如 ‘live’ -> /l ih v/ or /l ay v/ 中文中也有多音字的现象,所以需要进行标注。 -- **韵律分析** : 声调,重读,韵律边界 - - - -#### 语音合成方法 - -**波形拼接** : 将各种语音单元拼接起来,需要考虑目标代价(目标语音单元和候选的语音单元匹配度)和连接代价(相邻语音单元之间的流畅度) - -**基于轨迹指导的拼接合成** - -**统计参数合成** : 帧级建模包括时长模型(音素序列->帧级文本特征)和声学模型(帧级文本特征->帧级语音输出)。主要方法是基于HMM 的 SPSS (Statistical Parametric Speech Synthesis), 可以用的工具包 HTS。 - -**神经网络合成方法** : 目前许多商用场景下已经部署了基于神经网络的语音合成模型。目前基于神经网络的方法还不是纯端到端的,分为两个部分,输入文本类信息(音素,时长等)经过神经网络得到输出特征(LF0, UV, 谱特征, bap), 接着将这些特征放到声码器(vocoder) 中得到对应的语音波形。主流方法是 Tactron, Tactron2, 注意力机制,Transformer。正在朝着基于序列到序列的语音合成,纯端到端的语音合成方向发展。 - -**声码器**的总结如下: - -| **模型类型** | **模型** | **合成语音质量** | **效率** | -| ------------ | ----------------- | ---------------- | ---------- | -| AR | WaveNet | 非常好 | 非常差 | -| AR | WaveRNN | 非常好 | 中等 | -| AR | Multiband WaveRNN | 非常好 | 中等 | -| AR | LPCNET | 非常好 | 挺好的 | -| Non-AR | Parallel WaveNet | 非常好 | 还不错 | -| Non-AR | WaveGlow | 非常好 | 还不错 | -| Non-AR | FlowWaveNet | 非常好 | 还不错 | -| GAN | ParallelWaveGAN | 非常好 | 挺好的 | -| GAN | MelGAN | 挺好的 | 非常好 | -| GAN | MB-MelGAN | 非常好 | 非常非常好 | - -从上面表格中可以看到基于神经网络的声码器效果都挺好的,主要需要优化的就是生成的速度。出现了利用GAN的声码器之后,推理速度也极大的提高了。 - - - -### 高阶话题 - -* 基于注意力机制的序列要序列的模型框架稳定性问题: 长句、连读、丢字、漏字、重复 - -* 小样本学习(few shots & one shot) - -* 情感/表现力/可控性(句子内部细粒度控制,风格建模) - -* 纯端到端 - -* 抗噪 - -* 语音转换 - -* 歌唱合成 - - - -### 语音合成评估 - -文本分析模块可以有比较客观的指标:precision, recall, fscore 之类的。 - -生成的语音质量评估方法有:和参考样例之间的距离度量(DTW), 谱包络(MCD), F0轮廓,V/UV Error, 时长 (Duration RMSE)。 - -主观指标包括 MOS,CMOS, AB Best, MUSHRA。 - - - -### 语音合成数据集 - -数据质量非常重要 - -中文: 标贝DB-1,女性说话,1万句,10.3小时 - -英文: VCTK, LJSpeech, LibriSpeech, LibriTTS - - - -### 非端到端的语音合 - -目前非端到端的语音合成算法有两种, - -1)**参数语音合成方法**,其中*声学模型*包括基于隐马尔可夫(HMM)的统计参数语音合成和基于神经网络(NN)的统计参数语音合成,而*声码器*包括基于源-滤波器的声码器和基于NN的声码器 - -2) **单元拼接语音合成方法** 简单地理解是有一个很大的语音库包含了许多词/音素的发音,用一些方法将各个单元拼接起来。 - - - -#### 声学特征 - -传统声学模型这里的声学特征主要包括 MGC-梅尔生成倒谱, MCEP-梅尔倒谱, LSP-线谱对,这些普参数加上激励参数如基频F0,就是需要拟合的声学特征。而我们的音频通常都是一个个的采样点,谱参数+激励参数是可以还原到音频采样点的。 - -常用的工具:Straight, World, SPTK, [HTS](http://hts.sp.nitech.ac.jp/), [Pysptk](https://github.com/r9y9/pysptk)。 - - - -#### 基于HMM的统计参数语音合成 - -HMM 应用到 TTS 这里和 ASR 还是有些区别的。主要参考的论文是 [An Introduction to HMM-Based Speech Synthesis](https://www.researchgate.net/publication/265398553_An_Introduction_to_HMM-Based_Speech_Synthesis): - - - -#### 基于 NN 的参数语音合成 - -基于 NN 的参数语音合成主要依赖时长模型和声学模型。 - - - -### 风格化和个性化语音合成 - -风格化和个性化语音合成,难点有三个方面: - -- 风格化: 需要合成丰富且可控的语音,包括语速、停顿、重音、情感等。 -- 个性化: 要求我们利用多说话人建模技术及说话人自适应技术,在少量录音室或非录音室数据的条件下,为某一新说话人定制语音合成模型。 -- 迁移学习: 在只有一种语言的训练数据集下让说话人说另一种语言或者让说话人学习另一说话人的风格。迁移学习使我们能够利用额外的数据进行知识迁移,进而完成一些特定任务。 - -建模和评估比较困难、数据集标注成本高,标注人员对风格问题容易产生分歧、模型缺乏控制合成语音风格的能力。 - - - -## Reference - -* https://slyne.github.io/%E5%85%AC%E5%BC%80%E8%AF%BE/2020/09/26/TTS/ -* https://slyne.github.io/%E5%85%AC%E5%BC%80%E8%AF%BE/2020/10/25/TTS2/ -* https://slyne.github.io/%E5%85%AC%E5%BC%80%E8%AF%BE/2020/12/04/TTS6/ diff --git a/doc/src/tools.md b/doc/src/tools.md deleted file mode 100644 index 5fcca923..00000000 --- a/doc/src/tools.md +++ /dev/null @@ -1,3 +0,0 @@ -# Useful Tools - -* [正则可视化和常用正则表达式](https://wangwl.net/static/projects/visualRegex/#) diff --git a/doc/src/tts_text_front_end.md b/doc/src/tts_text_front_end.md deleted file mode 100644 index b13ab615..00000000 --- a/doc/src/tts_text_front_end.md +++ /dev/null @@ -1,212 +0,0 @@ -# Text Front End - - - -## Text Segmentation - -There are various libraries including some of the most popular ones like NLTK, Spacy, Stanford CoreNLP that that provide excellent, easy to use functions for sentence segmentation. - -* https://github.com/bminixhofer/nnsplit -* [DeepSegment](https://github.com/notAI-tech/deepsegment) [blog](http://bpraneeth.com/projects/deepsegment) [1](https://praneethbedapudi.medium.com/deepcorrection-1-sentence-segmentation-of-unpunctuated-text-a1dbc0db4e98) [2](https://praneethbedapudi.medium.com/deepcorrection2-automatic-punctuation-restoration-ac4a837d92d9) [3](https://praneethbedapudi.medium.com/deepcorrection-3-spell-correction-and-simple-grammar-correction-d033a52bc11d) [4](https://praneethbedapudi.medium.com/deepsegment-2-0-multilingual-text-segmentation-with-vector-alignment-fd76ce62194f) - - - -## Text Normalization(文本正则) - -The **basic preprocessing steps** that occur in English NLP, including data cleaning, stemming/lemmatization, tokenization and stop words. **not all of these steps are necessary for Chinese text data!** - -### Lexicon Normalization - -There’s a concept similar to stems in this language, and they’re called Radicals. **Radicals are basically the building blocks of Chinese characters.** All Chinese characters are made up of a finite number of components which are put together in different orders and combinations. Radicals are usually the leftmost part of the character. There are around 200 radicals in Chinese, and they are used to index and categorize characters. - -Therefore, procedures like stemming and lemmatization are not useful for Chinese text data because seperating the radicals would **change the word’s meaning entirely**. - -### Tokenization - -**Tokenizing breaks up text data into shorter pre-set strings**, which help build context and meaning for the machine learning model. - -These “tags” label the part of speech. There are 24 part of speech tags and 4 proper name category labels in the `**jieba**` package’s existing dictionary. - - - -### Stop Words - -In NLP, **stop words are “meaningless” words** that make the data too noisy or ambiguous. - -Instead of manually removing them, you could import the `**stopwordsiso**` package for a full list of Chinese stop words. More information can be found [here](https://pypi.org/project/stopwordsiso/). And with this, we can easily create code to filter out any stop words in large text data. - -```python -!pip install stopwordsiso -import stopwordsiso -from stopwordsiso import stopwords -stopwords(["zh"]) # Chinese -``` - - - -文本正则化 文本正则化主要是讲非标准词(NSW)进行转化,比如: - -1. 数字、电话号码: 10086 -> 一千零八十六/幺零零八六 -2. 时间,比分: 23:20 -> 二十三点二十分/二十三比二十 -3. 分数、小数、百分比: 3/4 -> 四分之三,3.24 -> 三点一四, 15% -> 百分之十五 -4. 符号、单位: ¥ -> 元, kg -> 千克 -5. 网址、文件后缀: www. -> 三W点 - -其他转换: - -1. 简体和繁体转换:中国语言 -> 中國語言 -2. 半角和全角准换:, -> , - -### tools - -* https://github.com/google/re2 - -* https://github.com/speechio/chinese_text_normalization - -* [vinorm](https://github.com/NoahDrisort/vinorm) [cpp_verion](https://github.com/NoahDrisort/vinorm_cpp_version) - - Python package for text normalization, use for frontend of Text-to-speech Reseach - -* https://github.com/candlewill/CNTN - - This is a ChiNese Text Normalization (CNTN) tool for Text-to-speech system, which is based on [sparrowhawk](https://github.com/google/sparrowhawk). - -* [Simplified and Traditional Chinese Characters converter](https://github.com/berniey/hanziconv) -* [Halfwidth and Fullwidth](https://zh.wikipedia.org/wiki/%E5%85%A8%E5%BD%A2%E5%92%8C%E5%8D%8A%E5%BD%A2) - - - -## Word Segmentation(分词) - -分词之所以重要可以通过这个例子来说明: -广州市长隆马戏欢迎你 -> 广州市 长隆 马戏 欢迎你 -如果没有分词错误会导致句意完全不正确: -广州 市长 隆马戏 欢迎你 - -分词常用方法分为最大前向匹配(基于字典)和基于CRF的分词方法。用CRF的方法相当于是把这个任务转换成了序列标注,相比于基于字典的方法好处是对于歧义或者未登录词有较强的识别能力,缺点是不能快速fix bug,并且性能略低于词典。 - - -中文分词的常见工具: -* https://github.com/lancopku/PKUSeg-python -* https://github.com/thunlp/THULAC-Python -* https://github.com/fxsjy/jieba -* CRF++ -* https://github.com/isnowfy/snownlp - -### MMSEG -* [MMSEG: A Word Identification System for Mandarin Chinese Text Based on Two Variants of the Maximum Matching Algorithm](http://technology.chtsai.org/mmseg/) -* [`中文分词`简单高效的MMSeg](https://www.cnblogs.com/en-heng/p/5872308.html) -* [mmseg分词算法及实现](https://blog.csdn.net/daniel_ustc/article/details/50488040) -* [Mmseg算法](https://www.jianshu.com/p/e4ae8d194487) -* [浅谈中文分词](http://www.isnowfy.com/introduction-to-chinese-segmentation/) - -* [pymmseg-cpp](https://github.com/pluskid/pymmseg-cpp.git) -* [ustcdane/mmseg](https://github.com/ustcdane/mmseg) -* [jkom-cloud/mmseg](https://github.com/jkom-cloud/mmseg) - - -### CScanner -* [CScanner - A Chinese Lexical Scanner](http://technology.chtsai.org/cscanner/) - - - -## Part of Speech(词性预测) - -词性解释 - -``` -n/名词 np/人名 ns/地名 ni/机构名 nz/其它专名 -m/数词 q/量词 mq/数量词 t/时间词 f/方位词 s/处所词 -v/动词 a/形容词 d/副词 h/前接成分 k/后接成分 -i/习语 j/简称 r/代词 c/连词 p/介词 u/助词 y/语气助词 -e/叹词 o/拟声词 g/语素 w/标点 x/其它 -``` - - - -## G2P(注音) - -注音是需要将词转换成对应的发音,对于中文是将其转换成拼音,比如 绿色->(lv4 se4) 这里的数字表示声调。 - -传统方法是使用字典,但是对于未登录词就很难解决。基于模型的方法是使用 [Phonetisaurus](https://github.com/AdolfVonKleist/Phonetisaurus)。 论文可以参考 - WFST-based Grapheme-to-Phoneme Conversion: Open Source Tools for Alignment, Model-Building and Decoding - -当然这个问题也可以看做是序列标注用CRF或者基于神经网络的模型都可以做。 基于神经网络工具: - -* https://github.com/kakaobrain/g2pM -* https://github.com/Kyubyong/g2p - - - - -## Prosody(韵律预测) - -ToBI(an abbreviation of tones and break indices) is a set of conventions for transcribing and annotating the prosody of speech. 中文主要关注break。 - -韵律等级结构: - -``` -音素 -> 音节 -> 韵律词(Prosody Word, PW) -> 韵律短语(prosody phrase, PPH) -> 语调短句(intonational phrase, IPH) -> 子句子 -> 主句子 -> 段落 -> 篇章 -LP -> LO -> L1(#1) -> L2(#2) -> L3(#3) -> L4(#4) -> L5 -> L6 -> L7 -``` - - -主要关注 PW, PPH, IPH - -| | 停顿时长 | 前后音高特征 | -| --- | ----------| --- | -| 韵律词边界 | 不停顿或从听感上察觉不到停顿 | 无 | -| 韵律短语边界 | 可以感知停顿,但无明显的静音段 | 音高不下倾或稍下倾,韵末不可做句末 | -| 语调短语边界 | 有较长停顿 | 音高下倾比较完全,韵末可以作为句末 | - -常用方法使用的是级联CRF,首先预测如果是PW,再继续预测是否是PPH,再预测是否是IPH - - - -论文: 2015 .Ding Et al. - Automatic Prosody Prediction For Chinese Speech Synthesis Using BLSTM-RNN and Embedding Features - - - -## Polyphone(多音字) - - - -## Linguistic Features(语言学特征) - - - -## 基于神经网络的前端文本分析模型 - -最近这两年基本都是基于 BERT,所以这里记录一下相关的论文: - -- g2p: 2019. Sevinj Et al. Transformer based Grapheme-to-Phoneme Conversion -- 分词: 2019 huang Et al. - Toward Fast and Accurate Neural Chinese Word Segmentation with Multi-Criteria Learning -- 韵律: 2020 Zhang Et al. - Chinese Prosodic Structure Prediction Based on a Pretrained Language Representation Model - -除此之外,BLSTM + CRF 也比较主流。 - - - - - -## 总结 - -总结一下,文本分析各个模块的方法: - -TN: 基于规则的方法 - -分词: 字典/CRF/BLSTM+CRF/BERT - -注音: ngram/CRF/BLSTM/seq2seq - -韵律: CRF/BLSTM + CRF/ BERT - - - -考虑到分词,注音,韵律都是基于序列标注任务,所以理论上来说可以通过一个模型搞定。 - - - -## Reference -* [Text Front End](https://slyne.github.io/%E5%85%AC%E5%BC%80%E8%AF%BE/2020/10/03/TTS1/) -* [Chinese Natural Language (Pre)processing: An Introduction](https://towardsdatascience.com/chinese-natural-language-pre-processing-an-introduction-995d16c2705f) -* [Beginner’s Guide to Sentiment Analysis for Simplified Chinese using SnowNLP](https://towardsdatascience.com/beginners-guide-to-sentiment-analysis-for-simplified-chinese-using-snownlp-ce88a8407efb) diff --git a/doc/src/tts_text_frontend.md b/doc/src/tts_text_frontend.md deleted file mode 100644 index e942469e..00000000 --- a/doc/src/tts_text_frontend.md +++ /dev/null @@ -1,215 +0,0 @@ -# Text Front End - - - -## Text Segmentation - -There are various libraries including some of the most popular ones like NLTK, Spacy, Stanford CoreNLP that that provide excellent, easy to use functions for sentence segmentation. - -* https://github.com/bminixhofer/nnsplit -* [DeepSegment](https://github.com/notAI-tech/deepsegment) [blog](http://bpraneeth.com/projects/deepsegment) [1](https://praneethbedapudi.medium.com/deepcorrection-1-sentence-segmentation-of-unpunctuated-text-a1dbc0db4e98) [2](https://praneethbedapudi.medium.com/deepcorrection2-automatic-punctuation-restoration-ac4a837d92d9) [3](https://praneethbedapudi.medium.com/deepcorrection-3-spell-correction-and-simple-grammar-correction-d033a52bc11d) [4](https://praneethbedapudi.medium.com/deepsegment-2-0-multilingual-text-segmentation-with-vector-alignment-fd76ce62194f) - - - -## Text Normalization(文本正则) - -The **basic preprocessing steps** that occur in English NLP, including data cleaning, stemming/lemmatization, tokenization and stop words. **not all of these steps are necessary for Chinese text data!** - -### Lexicon Normalization - -There’s a concept similar to stems in this language, and they’re called Radicals. **Radicals are basically the building blocks of Chinese characters.** All Chinese characters are made up of a finite number of components which are put together in different orders and combinations. Radicals are usually the leftmost part of the character. There are around 200 radicals in Chinese, and they are used to index and categorize characters. - -Therefore, procedures like stemming and lemmatization are not useful for Chinese text data because seperating the radicals would **change the word’s meaning entirely**. - -### Tokenization - -**Tokenizing breaks up text data into shorter pre-set strings**, which help build context and meaning for the machine learning model. - -These “tags” label the part of speech. There are 24 part of speech tags and 4 proper name category labels in the `**jieba**` package’s existing dictionary. - - - -### Stop Words - -In NLP, **stop words are “meaningless” words** that make the data too noisy or ambiguous. - -Instead of manually removing them, you could import the `**stopwordsiso**` package for a full list of Chinese stop words. More information can be found [here](https://pypi.org/project/stopwordsiso/). And with this, we can easily create code to filter out any stop words in large text data. - -```python -!pip install stopwordsiso -import stopwordsiso -from stopwordsiso import stopwords -stopwords(["zh"]) # Chinese -``` - - - -文本正则化 文本正则化主要是讲非标准词(NSW)进行转化,比如: - -1. 数字、电话号码: 10086 -> 一千零八十六/幺零零八六 -2. 时间,比分: 23:20 -> 二十三点二十分/二十三比二十 -3. 分数、小数、百分比: 3/4 -> 四分之三,3.24 -> 三点一四, 15% -> 百分之十五 -4. 符号、单位: ¥ -> 元, kg -> 千克 -5. 网址、文件后缀: www. -> 三W点 - -其他转换: - -1. 简体和繁体转换:中国语言 -> 中國語言 -2. 半角和全角准换:, -> , - -### tools - -* https://github.com/google/re2 - -* https://github.com/speechio/chinese_text_normalization - -* [vinorm](https://github.com/NoahDrisort/vinorm) [cpp_verion](https://github.com/NoahDrisort/vinorm_cpp_version) - - Python package for text normalization, use for frontend of Text-to-speech Reseach - -* https://github.com/candlewill/CNTN - - This is a ChiNese Text Normalization (CNTN) tool for Text-to-speech system, which is based on [sparrowhawk](https://github.com/google/sparrowhawk). - -* [Simplified and Traditional Chinese Characters converter](https://github.com/berniey/hanziconv) - -* [Halfwidth and Fullwidth](https://zh.wikipedia.org/wiki/%E5%85%A8%E5%BD%A2%E5%92%8C%E5%8D%8A%E5%BD%A2) - -* https://github.com/BYVoid/OpenCC - - - -## Word Segmentation(分词) - -分词之所以重要可以通过这个例子来说明: -广州市长隆马戏欢迎你 -> 广州市 长隆 马戏 欢迎你 -如果没有分词错误会导致句意完全不正确: -广州 市长 隆马戏 欢迎你 - -分词常用方法分为最大前向匹配(基于字典)和基于CRF的分词方法。用CRF的方法相当于是把这个任务转换成了序列标注,相比于基于字典的方法好处是对于歧义或者未登录词有较强的识别能力,缺点是不能快速fix bug,并且性能略低于词典。 - - -中文分词的常见工具: -* https://github.com/lancopku/PKUSeg-python -* https://github.com/thunlp/THULAC-Python -* https://github.com/fxsjy/jieba -* CRF++ -* https://github.com/isnowfy/snownlp - -### MMSEG -* [MMSEG: A Word Identification System for Mandarin Chinese Text Based on Two Variants of the Maximum Matching Algorithm](http://technology.chtsai.org/mmseg/) -* [`中文分词`简单高效的MMSeg](https://www.cnblogs.com/en-heng/p/5872308.html) -* [mmseg分词算法及实现](https://blog.csdn.net/daniel_ustc/article/details/50488040) -* [Mmseg算法](https://www.jianshu.com/p/e4ae8d194487) -* [浅谈中文分词](http://www.isnowfy.com/introduction-to-chinese-segmentation/) - -* [pymmseg-cpp](https://github.com/pluskid/pymmseg-cpp.git) -* [ustcdane/mmseg](https://github.com/ustcdane/mmseg) -* [jkom-cloud/mmseg](https://github.com/jkom-cloud/mmseg) - - -### CScanner -* [CScanner - A Chinese Lexical Scanner](http://technology.chtsai.org/cscanner/) - - - -## Part of Speech(词性预测) - -词性解释 - -``` -n/名词 np/人名 ns/地名 ni/机构名 nz/其它专名 -m/数词 q/量词 mq/数量词 t/时间词 f/方位词 s/处所词 -v/动词 a/形容词 d/副词 h/前接成分 k/后接成分 -i/习语 j/简称 r/代词 c/连词 p/介词 u/助词 y/语气助词 -e/叹词 o/拟声词 g/语素 w/标点 x/其它 -``` - - - -## G2P(注音) - -注音是需要将词转换成对应的发音,对于中文是将其转换成拼音,比如 绿色->(lv4 se4) 这里的数字表示声调。 - -传统方法是使用字典,但是对于未登录词就很难解决。基于模型的方法是使用 [Phonetisaurus](https://github.com/AdolfVonKleist/Phonetisaurus)。 论文可以参考 - WFST-based Grapheme-to-Phoneme Conversion: Open Source Tools for Alignment, Model-Building and Decoding - -当然这个问题也可以看做是序列标注用CRF或者基于神经网络的模型都可以做。 基于神经网络工具: - -* https://github.com/kakaobrain/g2pM -* https://github.com/Kyubyong/g2p - - - - -## Prosody(韵律预测) - -ToBI(an abbreviation of tones and break indices) is a set of conventions for transcribing and annotating the prosody of speech. 中文主要关注break。 - -韵律等级结构: - -``` -音素 -> 音节 -> 韵律词(Prosody Word, PW) -> 韵律短语(prosody phrase, PPH) -> 语调短句(intonational phrase, IPH) -> 子句子 -> 主句子 -> 段落 -> 篇章 -LP -> LO -> L1(#1) -> L2(#2) -> L3(#3) -> L4(#4) -> L5 -> L6 -> L7 -``` - - -主要关注 PW, PPH, IPH - -| | 停顿时长 | 前后音高特征 | -| --- | ----------| --- | -| 韵律词边界 | 不停顿或从听感上察觉不到停顿 | 无 | -| 韵律短语边界 | 可以感知停顿,但无明显的静音段 | 音高不下倾或稍下倾,韵末不可做句末 | -| 语调短语边界 | 有较长停顿 | 音高下倾比较完全,韵末可以作为句末 | - -常用方法使用的是级联CRF,首先预测如果是PW,再继续预测是否是PPH,再预测是否是IPH - - - -论文: 2015 .Ding Et al. - Automatic Prosody Prediction For Chinese Speech Synthesis Using BLSTM-RNN and Embedding Features - - - -## Polyphone(多音字) - - - -## Linguistic Features(语言学特征) - - - -## 基于神经网络的前端文本分析模型 - -最近这两年基本都是基于 BERT,所以这里记录一下相关的论文: - -- g2p: 2019. Sevinj Et al. Transformer based Grapheme-to-Phoneme Conversion -- 分词: 2019 huang Et al. - Toward Fast and Accurate Neural Chinese Word Segmentation with Multi-Criteria Learning -- 韵律: 2020 Zhang Et al. - Chinese Prosodic Structure Prediction Based on a Pretrained Language Representation Model - -除此之外,BLSTM + CRF 也比较主流。 - - - - - -## 总结 - -总结一下,文本分析各个模块的方法: - -TN: 基于规则的方法 - -分词: 字典/CRF/BLSTM+CRF/BERT - -注音: ngram/CRF/BLSTM/seq2seq - -韵律: CRF/BLSTM + CRF/ BERT - - - -考虑到分词,注音,韵律都是基于序列标注任务,所以理论上来说可以通过一个模型搞定。 - - - -## Reference -* [Text Front End](https://slyne.github.io/%E5%85%AC%E5%BC%80%E8%AF%BE/2020/10/03/TTS1/) -* [Chinese Natural Language (Pre)processing: An Introduction](https://towardsdatascience.com/chinese-natural-language-pre-processing-an-introduction-995d16c2705f) -* [Beginner’s Guide to Sentiment Analysis for Simplified Chinese using SnowNLP](https://towardsdatascience.com/beginners-guide-to-sentiment-analysis-for-simplified-chinese-using-snownlp-ce88a8407efb) \ No newline at end of file diff --git a/doc/src/vad.md b/doc/src/vad.md deleted file mode 100644 index e73e9cf7..00000000 --- a/doc/src/vad.md +++ /dev/null @@ -1,31 +0,0 @@ -# VAD - -## Endpoint Detection - -### Kaldi - - **Kaldi**使用规则方式,制定了五条规则,只要满足其中一条则认为是检测到了 endpoint。 - -1. 识别出文字之前,检测到了 5s 的静音; -2. 识别出文字之后,检测到了 2s 的静音; -3. 解码到概率较小的 final state,且检测到了 1s 的静音; -4. 解码到概率较大的 final state,且检测到了 0.5s 的静音; -5. 已经解码了 20s。 - -### CTC - -将连续的长 blank 标签,视为非语音区域。非语音区域满足一定的条件,即可认为是检测到了 endpoint。同时,参考 Kaldi 的 `src/online2/online-endpoint.h`,制定了以下三条规则: - -1. 识别出文字之前,检测到了 5s 的静音; -2. 识别出文字之后,检测到了 1s 的静音; -3. 已经解码了 20s。 - -只要满足上述三条规则中的任意一条, 就认为检测到了 endpoint。 - - - -## Reference - -* [Endpoint 检测](https://mp.weixin.qq.com/s?__biz=MzU2NjUwMTgxOQ==&mid=2247484024&idx=1&sn=12da2ee76347de4a18856274ba6ba61f&chksm=fcaacaaccbdd43ba6b3e996bbf1e2ac6d5f1b449dfd80fcaccfbbe0a240fa1668b931dbf4bd5&scene=21#wechat_redirect) -* Kaldi: *https://github.com/kaldi-asr/kaldi/blob/6260b27d146e466c7e1e5c60858e8da9fd9c78ae/src/online2/online-endpoint.h#L132-L150* -* End-to-End Automatic Speech Recognition Integrated with CTC-Based Voice Activity Detection: *https://arxiv.org/pdf/2002.00551.pdf*