Pandora's box

Supo's Blogs


  • 首页

  • 归档

markdown数学公式语法

发表于 2019-11-03

markdown数学公式语法(转)

源:https://www.jianshu.com/p/e74eb43960a1

行内与独行

  1. 行内公式:将公式插入到本行内,符号:$公式内容$,如:$xyz$ (转者注:typora需要手动开启内联公式,文件->偏好设置 -> markdown -> markdown扩展语法)
  2. 独行公式:将公式插入到新的一行内,并且居中,符号:$$公式内容$$,如:$$xyz$$

上标、下标与组合

  1. 上标符号,符号:^,如:$x^4$
  2. 下标符号,符号:_,如:$x_1$
  3. 组合符号,符号:{},如:${16}{8}O{2+}{2}$

正上标与正下标

  1. 正上标,符号: \underset,如:$\underset{a}{b}$
  2. 正下标,符号: \upset,如:$\overset{a}{b}$

汉字、字体与格式

  1. 汉字形式,符号:\mbox{},如:$V_{\mbox{初始}}$
  2. 字体控制,符号:\displaystyle,如:$\displaystyle \frac{x+y}{y+z}$
  3. 下划线符号,符号:\underline,如:$\underline{x+y}$
  4. 标签,符号\tag{数字},如:$\tag{11}$
  5. 上大括号,符号:\overbrace{算式},如:$\overbrace{a+b+c+d}^{2.0}$
  6. 下大括号,符号:\underbrace{算式},如:$a+\underbrace{b+c}_{1.0}+d$
  7. 上位符号,符号:\stacrel{上位符号}{基位符号},如:$\vec{x}\stackrel{\mathrm{def}}{=}{x_1,\dots,x_n}$

占位符

  1. 两个quad空格,符号:\qquad,如:$x \qquad y$
  2. quad空格,符号:\quad,如:$x \quad y$
  3. 大空格,符号\,如:$x \ y$
  4. 中空格,符号\:,如:$x : y$
  5. 小空格,符号\,,如:$x , y$
  6. 没有空格,符号,如:$xy$
  7. 紧贴,符号\!,如:$x ! y$

定界符与组合

  1. 括号,符号:()\big(\big) \Big(\Big) \bigg(\bigg) \Bigg(\Bigg),如:$()\big(\big) \Big(\Big) \bigg(\bigg) \Bigg(\Bigg)$
  2. 中括号,符号:[],如:$[x+y]$
  3. 大括号,符号:\{ \},如:${x+y}$
  4. 自适应括号,符号:\left \right,如:$\left(x\right)$,$\left(x{yz}\right)$
  5. 组合公式,符号:{上位公式 \choose 下位公式},如:${n+1 \choose k}={n \choose k}+{n \choose k-1}$
  6. 组合公式,符号:{上位公式 \atop 下位公式},如:$\sum_{k_0,k_1,\ldots>0 \atop k_0+k_1+\cdots=n}A_{k_0}A_{k_1}\cdots$

四则运算

  1. 加法运算,符号:+,如:$x+y=z$
  2. 减法运算,符号:-,如:$x-y=z$
  3. 加减运算,符号:\pm,如:$x \pm y=z$
  4. 减甲运算,符号:\mp,如:$x \mp y=z$
  5. 乘法运算,符号:\times,如:$x \times y=z$
  6. 点乘运算,符号:\cdot,如:$x \cdot y=z$
  7. 星乘运算,符号:\ast,如:$x \ast y=z$
  8. 除法运算,符号:\div,如:$x \div y=z$
  9. 斜法运算,符号:/,如:$x/y=z$
  10. 分式表示,符号:\frac{分子}{分母},如:$\frac{x+y}{y+z}$
  11. 分式表示,符号:{分子} \voer {分母},如:${x+y} \over {y+z}$
  12. 绝对值表示,符号:||,如:$|x+y|$

高级运算

  1. 平均数运算,符号:\overline{算式},如:$\overline{xyz}$
  2. 开二次方运算,符号:\sqrt,如:$\sqrt x$
  3. 开方运算,符号:\sqrt[开方数]{被开方数},如:$\sqrt[3]{x+y}$
  4. 对数运算,符号:\log,如:$\log(x)$
  5. 极限运算,符号:\lim,如:$\lim^{x \to \infty}_{y \to 0}{\frac{x}{y}}$
  6. 极限运算,符号:\displaystyle \lim,如:$\displaystyle \lim^{x \to \infty}_{y \to 0}{\frac{x}{y}}$
  7. 求和运算,符号:\sum,如:$\sum^{x \to \infty}_{y \to 0}{\frac{x}{y}}$
  8. 求和运算,符号:\displaystyle \sum,如:$\displaystyle \sum^{x \to \infty}_{y \to 0}{\frac{x}{y}}$
  9. 积分运算,符号:\int,如:$\int^{\infty}_{0}{xdx}$
  10. 积分运算,符号:\displaystyle \int,如:$\displaystyle \int^{\infty}_{0}{xdx}$
  11. 微分运算,符号:\partial,如:$\frac{\partial x}{\partial y}$
  12. 矩阵表示,符号:\begin{matrix} \end{matrix},如:$\left[ \begin{matrix} 1 &2 &\cdots &4\5 &6 &\cdots &8\vdots &\vdots &\ddots &\vdots\13 &14 &\cdots &16\end{matrix} \right]$

逻辑运算

  1. 等于运算,符号:=,如:$x+y=z$
  2. 大于运算,符号:>,如:$x+y>z$
  3. 小于运算,符号:<,如:$x+y<z$
  4. 大于等于运算,符号:\geq,如:$x+y \geq z$
  5. 小于等于运算,符号:\leq,如:$x+y \leq z$
  6. 不等于运算,符号:\neq,如:$x+y \neq z$
  7. 不大于等于运算,符号:\ngeq,如:$x+y \ngeq z$
  8. 不大于等于运算,符号:\not\geq,如:$x+y \not\geq z$
  9. 不小于等于运算,符号:\nleq,如:$x+y \nleq z$
  10. 不小于等于运算,符号:\not\leq,如:$x+y \not\leq z$
  11. 约等于运算,符号:\approx,如:$x+y \approx z$
  12. 恒定等于运算,符号:\equiv,如:$x+y \equiv z$

集合运算

  1. 属于运算,符号:\in,如:$x \in y$
  2. 不属于运算,符号:\notin,如:$x \notin y$
  3. 不属于运算,符号:\not\in,如:$x \not\in y$
  4. 子集运算,符号:\subset,如:$x \subset y$
  5. 子集运算,符号:\supset,如:$x \supset y$
  6. 真子集运算,符号:\subseteq,如:$x \subseteq y$
  7. 非真子集运算,符号:\subsetneq,如:$x \subsetneq y$
  8. 真子集运算,符号:\supseteq,如:$x \supseteq y$
  9. 非真子集运算,符号:\supsetneq,如:$x \supsetneq y$
  10. 非子集运算,符号:\not\subset,如:$x \not\subset y$
  11. 非子集运算,符号:\not\supset,如:$x \not\supset y$
  12. 并集运算,符号:\cup,如:$x \cup y$
  13. 交集运算,符号:\cap,如:$x \cap y$
  14. 差集运算,符号:\setminus,如:$x \setminus y$
  15. 同或运算,符号:\bigodot,如:$x \bigodot y$
  16. 同与运算,符号:\bigotimes,如:$x \bigotimes y$
  17. 实数集合,符号:\mathbb{R},如:\mathbb{R}
  18. 自然数集合,符号:\mathbb{Z},如:\mathbb{Z}
  19. 空集,符号:\emptyset,如:$\emptyset$

数学符号

  1. 无穷,符号:\infty,如:$\infty$
  2. 虚数,符号:\imath,如:$\imath$
  3. 虚数,符号:\jmath,如:$\jmath$
  4. 数学符号,符号\hat{a},如:$\hat{a}$
  5. 数学符号,符号\check{a},如:$\check{a}$
  6. 数学符号,符号\breve{a},如:$\breve{a}$
  7. 数学符号,符号\tilde{a},如:$\tilde{a}$
  8. 数学符号,符号\bar{a},如:$\bar{a}$
  9. 矢量符号,符号\vec{a},如:$\vec{a}$
  10. 数学符号,符号\acute{a},如:$\acute{a}$
  11. 数学符号,符号\grave{a},如:$\grave{a}$
  12. 数学符号,符号\mathring{a},如:$\mathring{a}$
  13. 一阶导数符号,符号\dot{a},如:$\dot{a}$
  14. 二阶导数符号,符号\ddot{a},如:$\ddot{a}$
  15. 上箭头,符号:\uparrow,如:$\uparrow$
  16. 上箭头,符号:\Uparrow,如:$\Uparrow$
  17. 下箭头,符号:\downarrow,如:$\downarrow$
  18. 下箭头,符号:\Downarrow,如:$\Downarrow$
  19. 左箭头,符号:\leftarrow,如:$\leftarrow$
  20. 左箭头,符号:\Leftarrow,如:$\Leftarrow$
  21. 右箭头,符号:\rightarrow,如:$\rightarrow$
  22. 右箭头,符号:\Rightarrow,如:$\Rightarrow$
  23. 底端对齐的省略号,符号:\ldots,如:$1,2,\ldots,n$
  24. 中线对齐的省略号,符号:\cdots,如:$x_1^2 + x_2^2 + \cdots + x_n^2$
  25. 竖直对齐的省略号,符号:\vdots,如:$\vdots$
  26. 斜对齐的省略号,符号:\ddots,如:$\ddots$

希腊字母

字母 实现 字母 实现
A A α \alhpa
B B β \beta
Γ \Gamma γ \gamma
Δ \Delta δ \delta
E E ϵ \epsilon
Z Z ζ \zeta
H H η \eta
Θ \Theta θ \theta
I I ι \iota
K K κ \kappa
Λ \Lambda λ \lambda
M M μ \mu
N N ν \nu
Ξ \Xi ξ \xi
O O ο \omicron
Π \Pi π \pi
P P ρ \rho
Σ \Sigma σ \sigma
T T τ \tau
Υ \Upsilon υ \upsilon
Φ \Phi ϕ \phi
X X χ \chi
Ψ \Psi ψ \psi
Ω \v ω \omega

EM算法的推导与应用

发表于 2019-11-03

EM算法的推导与应用

什么是EM算法

EM是一种迭代算法(梯度下降也是一种迭代算法),用于含有隐变量的概率模型参数的极大似然估计(极大后验概率估计)。EM算法由两部组成:E步,求期望(expectation);M步,求极大(maximization)。

概率模型有时既含有观测变量(observable variable),又含有隐变量或潜在变量(latent variable)。如果概率模型的变量都是观测变量,那么可以直接使用极大似然估计的方法估计模型参数。EM算法是含有隐变量的极大似然估计法。

一个例子

三硬币模型: 假设有三枚硬币,分别记作A,B,C,这些硬币正面出现的概率为$\pi$,$p$,$q$。进行如下掷硬币实验,先掷硬币A,如果A为正面选硬币B,如果A为反面,选硬币C,然后掷选出的硬币,出现正面记作1,反面记作0。独立重复n次实验,观测结果如下:
$$
1,1,0,1,0,0,1,0,1,1
$$
假设只能观测掷硬币的结果,不能观测掷硬币的过程,求三硬币正面出现的概率,即模型参数,$\pi$,$p$,$q$。

将观测数据表示为$Y=(Y_1,Y_2,…,Y_n)^T$,未观测数据表示为$Z=(Z_1,Z_2,…,Z_n)^T$,观测数据的似然函数为
$$
P(Y|\theta) = \sum_Z{P(Z|\theta)P(Y|Z,\theta)}
$$
即
$$
P(y|\theta) = {\pi{p^{y}}{(1-p)^{1-y}}} + {(1-\pi){q^y}{(1-q)^{1-y}}}
$$

一般用Y表示观测随机变量的数据,Z表示隐随机变量的数据。Y与Z连在一起称为完全数据,观测数据Y称为不完全数据。假设给定观测数据Y,其概率分布是$P(Y|\theta)$,其中$\theta$是需要估计的模型参数,那么不完全数据Y的似然函数是$P(Y|\theta)$,对数似然函数$L(\theta)=logP(Y|\theta)$;假设$Y$和$Z$的联合概率分布是$P(Y,Z|\theta)$,那么完全数据的对数似然函数是$logP(Y,Z|\theta)$。

EM算法是通过迭代求$L(\theta)=logP(Y|\theta)$的极大似然估计。

EM算法步骤

  • 步骤1:选择参数的初值$\theta^{(0)}$;初值可以任意选择,但是EM算法是初值敏感的。

  • 步骤2:E步,记$\theta^{(i)}$为第$i$次迭代参数$\theta$的估计值,在第$i+1$次迭代的E步,计算
    $$
    \begin{split}
    Q(\theta,\theta^{(i)}) &= E_Z{[logP(Y,Z|\theta)|Y,\theta^{(i)}]}\
    &=\sum_Z{logP(Y,Z|\theta)P(Z|Y,\theta^{(i)})}
    \end{split}
    $$

  • 步骤3:求使$Q(\theta,\theta^{(i+1)})$的极大化的$\theta$,确定第$i+1$次迭代的参数估计值
    $$
    \theta^{(i+1)} = arg\ \underset{\theta}{max}\ Q(\theta,\theta^)
    $$

  • 步骤4:重复第2步与第三步。

对话机器人的自然语言理解

发表于 2019-10-27 | 更新于 2019-10-28

对话机器人中的自然语言理解

简介:本人从2018年10月份从事自然语言处理行业,开始做的是语音助手。对自然语言理解有初步的了解。这是在博客里首次谈自己对自身行业的理解。

从图灵测试说对话机器人

图灵测试是指:如果一台机器能够与人类展开对话(通过电传设备)而不能被辨别出其机器身份,那么称这台机器具有智能。 这也是“智能”的定义,图灵测试为什么要采用对话的方式?而不是采用可以计算圆周率的100万位,判断一张图片里面是什么,或者把一段语音转化成文字。

这可以从语言说起,语言是区分人类和动物的重要标志,也是人具有高级智慧的象征。如果机器可以与人能够正常的对话,那我们确实可以说这个机器是智能的。

对话任务为什么比计算任务、分类任务和序列转化任务更难,甚至如果机器掌握的对话任务,就可以认为机器是智能的呢?

  • 计算任务,计算任务是计算机的最基础的能力,具有逻辑计算能力(与或非)、数字计算能力(加法器),最简单的单片机都具有这个能力,因此不能算是智能的标志。
  • 分类任务,分类任务基于计算任务,只不过这个任务稍微复杂一些,是通过合理的建模,将输入映射到0~N空间。
  • 序列转化任务是指序列标注、翻译、语言转文字等时序任务,如果不考虑时序,一些任务就会变得很糟糕,例如语音转文字中,一个读音可能对应多个文字,无法判断是哪个读音。在深度学习之前,这种时序性是通过马尔可夫模型解决的。可以说马尔可夫模型本身具有简单的推理能力,比如duihua,大概率是译成对话,而不是对画。但是这个推理还是在文字这个函数空间里面的,是有限定域的。

对话任务就比较复杂了,不但需要知识(数据库)、理解(NLU)、判断(分类),还需求高阶的推理能力,还要具有一定的情商,情商都需要哪些能力,还有待进一步探究。单说高阶的推理能力,足以否定市面上所有的机器人都不是人工智能。

丑话都说在前头了,目前人工智能远远达不到普通人的预期,但是在科研圈里还是很认可,对话机器人已经有了很大的进步。如果说对话机器人是自然语言处理的一个分支,那么推理能力是目前自然语言处理领域发展道路上的一个拦路虎。包括这两年横空出世的预处理模型,也是旨在解决语言中上下文的一个推理能力。

对话机器人技术剖析

说对话机器人是NLP领域皇冠上的明珠也是丝毫不过分的,因为涉及了NLP技术的方方面面,从基础的分词、命名实体识别,再到语义理解,对话管理,自然语言生成。中间插一句,这些技术都是应用在对话的各个阶段,前提是语音转化成文字,那能不能搞一个端到端的模型,直接从语音到语义呢?在搞一个模型,从语义到语音,中间就省下了一大堆步骤。这个是有可能的,从人类的角度,世界上是有很多语言没有文字的,而说这些语言的人也都是可以正常的交流表达复杂的观点。而且也有研究是这样做了,暂时识别的范围没有非端到端的模型好,但精度是高一些的。

1572276891269

暂时不提speech2meaning这种端到端的技术,对话机器人全流程可以分为5个层次,从上到下分别是用户层、语音层、语义层、对话层、数据层。

  • 用户层, 有两个实体,一个是用户及其所处的环境,一个是虚拟的对话机器人(chatbot)或者称为智能体(Agent)。
  • 语音层,两个功能,分别是语音转文字和文字转语音,这两个是语音识别的范畴,此篇文章不赘述。
  • 语义层,用户文本的浅层理解,以及将系统动作转化为回复话术,在这一层。
  • 对话层,包括两个功能,对话状态跟踪、动作策略。
  • 数据层,里面存储结构化知识库,例如知识图谱或某领域的数据库,例如火车时刻表。

对话机器人按技术可分为三类,一类是任务型对话机器人(task-oriented),是严格按照以上步骤来做的,如果不涉及知识,数据层是可以省略的。一类是检索型对话机器人,可以认为略过第三层语义层和第四层对话层,数据库内存储的是QA对,遇到一个用户话术,就检索出一个答案,回复给用户。一类是基于知识图谱问答机器人,对话层的处理比较少。每种技术都有其优缺点,但是

按类型可以分为,任务型、问答、闲聊,技术可以选择以上任何一种或者几种的集合。值得一提的是,在工业界,大部分语音助手以上几种技术都有涉及,类型也都覆盖全面。

语义层与对话层

今天我们重点的聊的就是语义层与对话层,之所以叫语义层(NLU)和对话层(DM),是行业内都这么称呼。其实这两侧的任务都是自然语言理解,只不过语义层做简单的自然语言理解(我们这样规定),对话层做的是语义推理。举个例子,对话状态跟踪器,是根据历史对话,当前语义,推理当前的对话状态;策略模块是根据当前对话状态,给出系统应该采取什么样的动作。

对话机器人的技术难点也就在对话层了,因为语义层可以归为分类任务(槽位提取可归为序列标注任务),而对话层做的是推理任务,下承接数据库,上考虑用户所处的环境,以及用户的语义信息。行业中目前的语音助手水平,对话管理的技术水平都不高,或者学术中也是对话推理的研究也没有到达很智能的水平,或许这就是行业的瓶颈,如果能够突破对话管理技术,相信对话机器人会被更多普通人接受为人工智能助理,而不是人工智障。

对话推理的方向

  • 知识库的扩充与统一检索,人是如何快速根据已有知识进行推理的呢?
  • 环境信息的编码,如何将环境信息编码在语义中,去影响对话的推理呢?

sed命令详解

发表于 2019-10-22 | 更新于 2019-10-23

sed命令详解

sed 是Stream EDitor的缩写,是一款古老的流编辑器,与之相对应的是交互式编辑器(如vim);流编辑器是按行读取的也称为行编辑器(sed, awk),相对应的概念是全文本编辑器。

sed参数解释

sed常用的参数:

参数 英文 解释
-n quiet 抑制模式空间的自动输出
-e script 增加执行脚本
-f script_file 执行脚本从文件中读取
-i in-place 替换原文件
-r regexp 支持正则

sed脚本命令解释

参数 英文 解释
s search 查找
g global 全局替换
p print 打印输出到模式空间
P print 打印到第一个换行符
i insert 插入
a append 附加文本
d delete 删除模式空间
D delete 如果模式空间为空读取新的一行,如果不为空则删除第一行,不读下一行,直接进入下一个循环。
h/H hold 将模式空间内容输出到保持空间
g/G get 将保持空间内容返回模式空间,大写G追加;小写g覆盖。
x exchange 交换模式空间与保持空间
n next 读取下一行到模式空间,覆盖模式
N next 读取下一行到模式空间,追加模式

sed搜索命令

命令示例 解释
s/old/new/ 只匹配每一行第一个,做替换
s/old/new/g 全局替换
s/old/new/2 匹配每一行第二次出现的字符,做替换
-n 's/old/new/p' 替换成功的行打印输出
/正表达式/s/old/new/g 复合正则表达式的行替换
行号s/old/new/ 指定行的替换
1,$s/old/new/ 第一行到最后一行的搜索替换
/正则表达式/1,$s/old/new/ 第一行到最后一行满足正则表达式的替换
-r 's/(a.*b)'/\1:\1 引用分组与回调

sed空间

1571762888018

sed多行模式

1
sed 'N; s/old/new/; P; D' file

每次处理 1,2行,2,3行…

1
sed 'N; N; s/old/new/; P; D' file

每次处理 123行,234行…

sed命令示例

查看奇/偶数行

1
2
sed 'n;d' staff.csv       # 奇数行
sed '1d;n;d' staff.csv # 偶数行

第一条命令中的n;表示输出当前行并立即读取下一行。第二条命令先把第一行记录删除,于是再输出的奇数行就自然变成原来的偶数行了。

从单行中查找

比如想从I am 18 years old里查找18这个年龄,可以这么做:

1
echo "I am 18 years old" | sed -n "s/I am \(.*\) years old/\1/p"

这里\1代表第一个被匹配上的内容也就是\(.*\)。发挥想象力:

1
echo "I am 18 years old" | sed -n "s/I am \(.*\) \(.*\) old/\2: \1/p"

C++中const总结

发表于 2019-10-13 | 更新于 2019-10-23

c++中const用法总结

引言

const在c++中是一种限定符(qualifier),用来修饰变量类型、形参类型、函数、指针、类等一系列用法,用的时候不是弄得很蒙,比如指向常量的指针(pointer to const)与常量指针(const pointer),因此在此文中区分这些用法,以及限定符的作用是什么。

基本含义

const 修饰意为恒定的,不变的,当修饰变量的时候,意思是该变量在创建后是不可修改的,因此常量的声明和定义是在一条语句内完成的。常量是指本身不可修改,但可以被拷贝,去为其他变量赋值。通常常量在编译的时候还会被字面量替代。

1
2
3
4
5
const int bufferSize = 512; // ok
const int bufferSize; //error
const int i = getSize();

int a = i; // ok copy const value to a variable

常量引用

想要引用常量,必须使用常量引用(reference to const),否则可能会改变原值。

1
2
3
const int a = 1;
const int &b = a; //ok,both reference and underlying object are const
int &c = a; //error unconst reference to a const object

常量引用的初始化可以是一个非常量、常量、字面量、表达式,常量只能给常量引用初始化。常量引用后面的真实引用值不一定是常量。

1
2
3
4
5
int i = 1;
const int &r1 = i; //ok
const int &r2 = 3; //ok
const int &r3 = r1*2; //ok
int &r4 = 3; //error

常量指针与常指针

常量指针与常量引用类似,不可以通过该引用或指针修改背后的值,但背后的值是不是常量是不确定的。

1
2
3
4
5
6
const double pi = 3.14;
double *p1 = &pi; //error
const double *ptr = &pi; // ok

double dval = 3.14;
ptr = &dval; //ok but cannt change dval through ptr

常指针是指该指针一旦绑定一个地址,就不可以再改变了,所以一定要在声明的时候初始化。做法是再*后面加const修饰符。

  • 常量指针是指不能改变指向的对象
  • 常指针是指不能改变指向的对象
1
2
3
4
5
int a = 0;
int *const b = &a;

const int c = 1;
const int *const cptr = &c;

常量表达式

常量表达式(constant expression)是值不可以改变而且在编译的时候评估该值。

1
2
3
4
const int max_files = 20; // max_files is constant expression
const int limit = max_file + 1; // limit is constant expression
int staff = 20; // staff is not constexpr
const in sz = get_size() //sz is not constexpr

可以用constexpr 声明一个变量是常量表达式,这样再编译的时候就会展开表达式。

类成员函数后面加const

表示const对象只能调用带const标记的函数,const函数可以重载。
例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class A {
public:
int a = 0, b = 0;
A() = default;
A(int _a, int _b): a(_a),b(_b) {
std::cout << "call constructor" << std::endl;
}

int get_a() const {
std::cout << "call const get_a" << std::endl;
return a;
}

int get_a() {
std::cout << "call get_a" << std::endl;
return a;
}

int get_b() {
return b;
}
};

int main() {
const A a;
A b(1 ,2);
std::cout << a.get_a() << std::endl; //ok
// std::cout << a.get_b() << std::endl; //error
std::cout << b.get_a() << std::endl; //ok
std::cout << b.get_b() << std::endl; //ok

}

google开源项目风格指南-C++

发表于 2019-10-09 | 更新于 2019-11-02

google开源项目风格指南-C++

引言

自从大学开始接触c++语言(平时写的都是类C代码),断断续续的已经有7、8年过去了,但是,始终没有规范的在工程上使用C++。现在的工作主要使用python语言,更多的是关注算法,而没有关注语言本身。但是python项目也给我带来了工程经验,经验之一就是,代码是写给别人看的,大家遵守共同的编程规范很重要。所谓的编程规范,也可以称为编程风格,一开始会让新手感觉是桎梏,但随着解了越来越多的bug,写了越来越多的代码,也体会到了,编程规范的而重要性,编程规范不但是一种风格,而且是避免埋坑、减少bug的重要措施。如果大家写的代码一致性高,那阅读起来也就方便多了。

在网上搜索了google的编程规范,应该是值得一读的,因为这些规范都是大厂内的有丰富工程经验的人总结出来的精华。因此,准备认真研读,并且,测试为什么这样规定。

头文件

一般来讲,每个cpp文件都应该对应一个.h文件(除了只包含main函数的文件),这是为了提升可读性与可维护性。一般头文件要遵守以下规范:

  • 头文件要加入#define保护,避免重复编译时导入;
  • 头文件只能声明变量,不能定义变量,全局的变量不可以声明,因为全局变量在声明时会初始化一个默认值。两次包含给头文件,编译时,会报重复定义的错误;
  • 头文件的包含顺序为自身头文件、c标准库、c++标准库、其他项目头文件、自身项目其他头文件;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* 为了防止被多重导入,header文件要有#define保护 */
#ifndef HEADER_A_H
#define HEADER_A_H 1

// 头文件里面不要定义任何变量,也不声明全局变量,全局变量会在声明时初始化为默认值
// C++原则-唯一定义原则(One Definition Rule), 但一个变量可以声明多次
//int i; //error 两次被引入会错误
//int j=0; //error 不能定义变量

void print(void); // ok, declear function

// ok , declear a class
class A {
int a,b;
public:
A(int a, int b);

};

#endif /* HEADER_A_H */

作用域

命名空间

命名空间(namespace)可以把全局作用域细分为具名独立的作用域,使用命名空间,可以避免一些通用命名的冲突。但两个不同命名空间的相同变量名称放在一起会有迷惑性,而且不得不引用多层嵌套命名空间内的定义会使代码变得冗长。

命名空间的两个特殊特性:

  • 内联命名空间:内敛命名空间会自动把标识符放在外部命名空间。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    namespace foo1{
    inline namespace foo2 {

    int c = 3;

    } //foo2

    int a = 1, b = 2;
    void print() {
    std::cout << a * b * c << std::endl;
    }

    } // foo1

    foo1::c 与 foo1::foo2::c一致
  • 匿名命名空间:编译器会给匿名命名空间生成一个独立不可见标识,导致,其他文件不能引用匿名命名空间内的变量,两个不同匿名命名空间的相同定义不会冲突(两个不同cpp文件)。

    1
    2
    3
    namespace {
    ...
    } //namespace

规范:

  • 禁止使用内联命名空间,在大型版本控制里面使用
  • 鼓励在.cc文件中使用匿名命名空间或static声明
  • 不能在header里面定义匿名命名空间,会违背唯一定义原则
  • 不能使用using指示命名空间
  • 不要在std里面声明任何任何东西,否则会导致不可移植
  • 不要在头文件中使用命名空间别名

非成员函数、静态成员函数、全局函数

使用静态成员函数或命名空间内的非成员函数, 尽量不要用裸的全局函数. 将一系列函数直接置于命名空间中,不要用类的静态方法模拟出命名空间的效果,类的静态方法应当和类的实例或静态数据紧密相关.

局部变量

将函数的变量尽可能置于最小的作用域内,并在变量声明时进行初始化。

不要在循环内部初始化对象,否则每次调用构造函数与析构函数导致执行效率很低

静态与全局变量

禁止定义静态存储周期的非POD(Plain Old Data),POD数据如int、char、float,多编译单元中的静态变量执行的构造与析构顺序时未明确的。也不使用vector和string类型的静态变量。

类

构造函数

不要再构造函数内调用虚函数,也不要进行可能会失败的初始化。因为这样不用考虑类是否被正确的初始化,经过构造函数初始化的类型可以是const类型,也可以方便的被其他容器调用。

如果调用虚函数,这类调用不会重定向到子类的虚函数实现;如果初始化可能会报错,则初始化的对象可能进入不正常的状态,必须调用bool isValid()判断,而这个方法容易被忽视;构造函数的地址是无法被获得的,有构造函数完成的工作无法以简单的方式交给其他线程。

运算符重载

除少数特定环境下,不要重载运算符,这与《More Effective C++》里面第5条款的要求是类似的。

运算符重载是指,c++允许用户使用operator关键字对内建运算符进行重载定义,operator允许用户使用operator""定义新的字面量,并且定义类型转换函数如:

1
2
operator bool(); // 重载bool类型转换
operator double(); //重载double类型转换

重载运算符可以使代码更简洁易懂,使用户自定义的类型与内建类型拥有相似的行为。

但是过度使用操作运算法重载,会导致令人迷惑的bug。

不要为了避免重载操作符而走极端. 比如说, 应当定义 ==, =, 和 << 而不是 Equals(), CopyFrom() 和 PrintTo(). 反过来说, 不要只是为了满足函数库需要而去定义运算符重载. 比如说, 如果你的类型没有自然顺序, 而你要将它们存入 std::set 中, 最好还是定义一个自定义的比较运算符而不是重载 <.

不要重载 &&, ||, , 或一元运算符 &. 不要重载 operator"", 也就是说, 不要引入用户定义字面量。

其他

  • 如果不需要拷贝,则把默认拷贝构造函数禁掉,使用= delete;
  • 不要使用多继承,少使用继承。

c++中类的相关概念

发表于 2019-10-06 | 更新于 2019-11-02

c++中类的相关概念

1. 类的定义

为什么类的定义后面一定要加“;”?

因为类后面是可以直接定义实例,但是并不建议这样定义,因为可读性比较差,变量最好单独定义。

1
2
3
4
5
// bad idea
class Sales_data {/*....*/} accum, trans, *saleptr;
// batter eay to define objects
class Sales_data {/*....*/};
Sales_data accum, trans, *saleptr;

2. 变量的初始化

变量后面直接跟着相应的数值成为初始化(initialize),是变量刚开始创建的时候,给的一个值。

1
2
3
4
// 可以使用前面的定义
double price = 109.9, discount = price * 0.26;
// 也可以使用函数的结果赋值
double salePrice = applyDiscount(price, discount);

c++中初始化(initialization)与赋值(assignment)是两个不同的概念,初始化是变量创建的时候发生的,赋值是值将变量的值替换。

初始化的方式有四种:

1
2
3
4
int units_sold = 0;
int units_sold(0);
int units_sold = {0};
int units_sold{0};

花括号的方式称为列表初始化,列表初始化,不会对字面量做转化。

1
2
int a{3.14}; // not allowed
int a = 3.14 // ok; value will be trunked

如果没有初始化,则会对变量进行默认初始化(default initializer)。

函数外部的变量会定义为0或空,函数内部的变量则不会进行初始化,未初始化的变量不能进行拷贝与访问内部成员。

  • 类内的成员变量如果没有显式初始化,则进行默认初始化,字符串类型变量为空,整形初始化为0 (GCC编译器并非如此)
  • 类内的成员变量初始化不允许‘()’形式

3. struct与class的区别

struct与class的位移区别是默认访问权限的区别,如果所有成员都默认为public则用struct字段,如果默认为私有则用class。

public定义接口(interface);

private封装(encapsulate)内部成员。

4. 构造函数

构造函数是指与类名相同,没有返回类型,具有参数列表,可以重载的函数。

当且仅当没有定义构造函数,则内部成员变量会进行默认初始化(default initializer)和类内初始化器(in-class initializer),默认初始化在默认构造函数内进行。在gcc上没有复现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A {
int a;
int b=2;
public:
void print() {
cout << "a is :" << a << ", b is :" << b << endl;
}
};

int main() {
A a;
a.print();
return 0;
}

结果:a is :-704482032, b is :2

如果自定义构造函数,则所有的内部成员变量都在构造函数内进行初始化。也可以不用

构造函数的几种写法:

1
2
3
4
5
6
struct A {
int a;
A() = default; //默认构造函数
A(int a) {this->a = a}
A(int a):a(a) {} //初始化列表
}

默认构造函数、析构函数对使用了动态内存的程序并不有效。

5. 类型转换

C++中有两种函数可以执行类型转换,一种为单变量的构造函数(或无默认值的参数),一种为类型转换运算符重载。

单变量的构造函数会为编译器提供一种隐式类型转换的方法,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

class Sale_data {
public:
Sale_data(const std::string str): book(str){
std::cout << "call Sale_data constructor" << std::endl;
}
std::string get_book() {
// std::cout << book << std::endl;
return book;
}
operator double() {
return 0.0;
}

//重载外部操作符 <<
friend std::ostream& operator<< (std::ostream& os, Sale_data a);
private:
std::string book;
};

std::ostream& operator<< (std::ostream& os, Sale_data a) {
os << a.get_book();
return os;
}

class Item {
public:
Item() = default;
void combine(Sale_data a) {
items.push_back(a);
}
void print() {
for (auto i: items){
std::cout << i << "\n";
}
std::cout << std::endl;
}
private:
std::vector<Sale_data> items = {};

};
int main() {
Item items;
Sale_data a = std::string("aaa"); // 正常初始化
items.combine(a); // 正常函数调用
items.combine(std::string("vbb")); //ok, 做了一次隐式类型转换,将string("vbb") 转换为Sale_data对象
//items.combine("aaaa"); //error 只允许一次类型转换,"aaa"不能自动转换为string再转换为Sale_data
items.print();
// a.get_book();
return 0;
}

C++11引入explicit显式声明不允许进行隐式类型转换,例如

1
2
3
4
5
6
7
8
9
10
11
12
class Sale_data {
explicit Sale_data(const std::string str): book(str){
std::cout << "call Sale_data constructor" << std::endl;
}
}

int main() {
std::string a = "aaaa";
Sale_data o = a; // error 不允许, 经实验并没有调用copy constructor,只是单纯的语法不允许
// items.combine(a); //error 不能进行隐式类型转换
items.combine(Sale_data(a)); //ok,显式转换,或者这就是在实例化一个类,生成一个对象
}

explicit还作用域列表初始化语法,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Sale_data {
explicit Sale_data(const std::string str, const int a): book(str), no(a) {}
}

void Foo(Sale_data);

int main() {
Sale_data a(std::string("bbb"))
Sale_data o = {std::string("aaa"), 111}; //error
Sale_data o{std::string("aaa"), 111}; //ok
Sale_data o{std::string("aaa"), a}; //ok, 因为我们在上面重载了一个double的类型转换操作符
Foo({std::string("aaa"), 111}); //error
}

使用vscode调试WSL内的C++程序

发表于 2019-10-04 | 更新于 2019-10-05

使用vscode调试WSL内的C++程序

主要内容

  • WSL配置
  • vscode配置

WSL配置

安装C++编译环境

1
apt install build-essential gdb

查看build-essential的依赖,可以看出的,build-essential是打包了gcc、g++、make等编译环境的工具。

1
2
3
4
5
6
7
8
9
10
root@DESKTOP-Q7IC13H:~/face_to_future# apt-cache depends build-essential
build-essential
|Depends: libc6-dev
Depends: <libc-dev>
libc6-dev
Depends: gcc
Depends: g++
Depends: make
make-guile
Depends: dpkg-dev

那么gcc与g++是什么关系呢

Several versions of the compiler (C, C++, Objective-C, Ada, Fortran, Java and treelang) are integrated; this is why we use the name “GNU Compiler Collection”. GCC can compile programs written in any of these languages. The Ada, Fortran, Java and treelang compilers are described in separate manuals.
“GCC” is a common shorthand term for the GNU Compiler Collection. This is both the most general name for the compiler, and the name used when the emphasis is on compiling C programs (as the abbreviation formerly stood for “GNU C Compiler”).
When referring to C++ compilation, it is usual to call the compiler “G++”. Since there is only one compiler, it is also accurate to call it “GCC” no matter what the language context; however, the term “G++” is more useful when the emphasis is on compiling C++ programs.
Similarly, when we talk about Ada compilation, we usually call the compiler “GNAT”, for the same reasons.
We use the name “GCC” to refer to the compilation system as a whole, and more specifically to the language-independent part of the compiler. For example, we refer to the optimization options as affecting the behavior of “GCC” or sometimes just “the compiler”.
Front ends for other languages, such as Mercury and Pascal exist but have not yet been integrated into GCC. These front ends, like that for C++, are built in subdirectories of GCC and link to it. The result is an integrated compiler that can compile programs written in C, C++, Objective-C, or any of the languages for which you have installed front ends.
In this manual, we only discuss the options for the C, Objective-C, and C++ compilers and those of the GCC core. Consult the documentation of the other front ends for the options to use when compiling programs written in other languages.
G++ is a compiler, not merely a preprocessor. G++ builds object code directly from your C++ program source. There is no intermediate C version of the program. (By contrast, for example, some other implementations use a program that generates a C program from your C++ source.) Avoiding an intermediate C representation of the program means that you get better object code, and better debugging information. The GNU debugger, GDB, works with this information in the object code to give you comprehensive C++ source-level editing capabilities (see C and C++ (Debugging with GDB)).

用man gcc 和man g++ 出来的页面是一样的,摘抄两段

gcc - GNU project C and C++ compiler.

The usual way to run GCC is to run the executable called gcc, or machine-gcc when cross-compiling, or machine-gcc-version to run a specific version of GCC. When you compile C++ programs, you should invoke GCC as g++ instead.

C++ source files conventionally use one of the suffixes .C, .cc, .cpp, .CPP, .c++, .cp, or .cxx; C++ header files often use .hh, .hpp, .H, or (for shared template code) .tcc; and preprocessed C++ files use the suffix .ii. GCC recognizes files with these names and compiles them as C++
programs even if you call the compiler the same way as for compiling C programs (usually with the name gcc).
However, the use of gcc does not add the C++ library. g++ is a program that calls GCC and automatically specifies linking against the C++library. It treats .c, .h and .i files as C++ source files instead of C source files unless -x is used. This program is also useful when precompiling a C header file with a .h extension for use in C++ compilations. On many systems, g++ is also installed with the name c++.

总之c++最好用g++编译,gcc继承了除了c很多语言的编译器

安装gdb

the GNU debugger, c与c++的调试工具

确认环境已配置正确

1
2
whereis g++
whereis gdb

vscode配置

.vscode 里面有三个文件需要配置

  • c_cpp_properties.json (compiler path and IntelliSense settings)
  • tasks.json (build instructions)
  • launch.json (debugger settings)

c/c++环境配置

按 Ctrl+Shift+P 命令行面板(Command Palette)
找到c/c++ Edit configurations(UI),修改里面配置,会同步到c_cpp_properties.json里面

1
2
3
complier path: /usr/bin/g++
intelliSense mode: gcc-x64
cStandard: c++17

编译环境配置

在命令行面板内输入task,选择Tasks:Configure Default Build Task,创建一个tasks.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"version": "2.0.0",
"windows": {
"options": {
"shell": {
"executable": "bash.exe",
"args": ["-c"]
}
}
},
"tasks": [
{
"label": "build hello world on WSL",
"type": "shell",
"command": "g++",
"args": [
"-g",
"-o",
"/home/<your linux user name>/projects/helloworld/helloworld.out",
"helloworld.cpp"
],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

debug设置配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "/home/<your Linux user name>/projects/helloworld/helloworld.out",
"args": [""],
"stopAtEntry": true,
"cwd": "/home/<your Linux user name>/projects/helloworld/",
"environment": [],
"externalConsole": true,
"windows": {
"MIMode": "gdb",
"miDebuggerPath": "/usr/bin/gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
},
"pipeTransport": {
"pipeCwd": "",
"pipeProgram": "c:\\windows\\sysnative\\bash.exe",
"pipeArgs": ["-c"],
"debuggerPath": "/usr/bin/gdb"
},
"sourceFileMap": {
"/mnt/c": "${env:systemdrive}/",
"/usr": "C:\\Users\\<path to WSL directory which you will place here later>"
}
}
]
}

参考:

https://code.visualstudio.com/docs/cpp/config-wsl

hyperV虚拟archlinux

发表于 2018-09-24

写在前面

很久没有体验过archlinux了,archlinux是我用过最好用的linux。升级不用重装系统,而且系统完全可自己定制,对于我这种爱折腾的人来讲最合适不过。老电脑寄回家了,手里只有一部穷人版surface,硬盘太小,不舍得装双系统,就在hyperV里体验一下吧。

1.Hyper-V

Hyper-V 支持在物理主机上运行虚拟化的计算机系统。 可以像在物理计算机系统上一样使用和管理这些虚拟化的系统,但它们存在于虚拟化和隔离的环境中。 称为虚拟机监控程序的特殊软件可管理虚拟系统和物理硬件资源之间的访问权限。 虚拟化支持快速部署计算机系统、快速将系统还原为先前已知的良好状态的方法和在物理主机之间迁移系统的能力。

虚拟化允许你:

  • 运行需要早期版本的 Windows 操作系统或非 Windows 操作系统的软件。
  • 实验其他操作系统。 通过 Hyper-V,可轻松创建和删除不同的操作系统。
  • 使用多个虚拟机在多个操作系统上测试软件。 通过 Hyper-V,可以在一部台式机或便携式计算机上运行所有内容。 可以将这些虚拟机导出并随后导入到任何其他 Hyper-V 系统中,包括 Azure。

一些限制限制:

  • 依赖于特定硬件的程序不能在虚拟机中良好运行。 例如,需要使用 GPU 进行处理的游戏或应用程序可能无法良好运行。 依赖于子 10 毫秒计时器的应用程序(如实时音乐混合应用程序或高精度时间)在虚拟机中运行时也可能会出问题。
  • 此外,如果已启用了 Hyper-V,这些易受延迟影响的高精度应用程序在主机中运行时可能也会出问题。 这是因为在启用了虚拟化后,主机操作系统也会在 Hyper-V 虚拟化层的顶部运行,就如来宾操作系统那样。 但是,与来宾操作系统不同,主机操作系统在这点上很特殊,它是直接访问所有硬件,这意味着具有特殊硬件要求的应用程序仍然可以在主机操作系统中运行,而不会出问题。

开启Hyper-V:

  • 右键单击 Windows 按钮并选择“应用和功能”。
  • 选择“打开或关闭 Windows 功能”。
  • 选择 Hyper-V,然后单击确定。

新建虚拟机:

  • 在装虚拟机之前需要新建一个虚拟交换机,用来连接网络。
  • 新建一个虚拟机,连接上虚拟交换机,挂在archlinux的ISO文件,关闭安全启动就可以开始安装arch了

2.安装archlinux

进行联网

执行:

1
# wifi-menu

连接wifi
或者:

1
# pppoe-setup

进行配置或者:

1
# systemctl start adsl

进行 adsl连接
连接完后,执行:

1
# ping archhlinux.org

或其他网址测试网络是否通

同步时间

执行:

1
# timedatectl set-ntp true

编辑镜像站文件

在镜像源网站加入中国的源例如清华tuna的、163的,放在mirrorlist的首行
执行:

1
# nano /etc/pacman.d/mirrorlist

nano一些简单的命令:

  • ALT+6: 复制
  • CTRL+U: 粘贴
  • CRTL+O: 写入文件
  • CRTL+X: 关闭文件

执行ctrl+x退出,提示 是否保存,输入y,回车 保存
完成后执行,以同步软件仓库

1
# pacman -Syy

开始分区(UEFI+GPT)

本次将为sda硬盘重新建立分区表,重新建立分区,数据会全部丢失.
分区方案:

分区 大小 挂载点
sda1 200M /mnt/boot/EFI
sda2 200M /mnt/boot
sda3 20G /mnt
sda4 10G /mnt/home

先查看下电脑硬盘设备,执行lsblk,如下图所示:(不同电脑设备不同,有可能会是 /dev/sdb……)
(有parted、fdisk两种分区方法,本次采用fdisk进行分区)

用fdisk进行分区
(1)建立GPT分区表
执行:

1
# fdisk /dev/sda1

不同电脑设备不同,有可能会是 /dev/sdb……)
进入fdisk交互界面:
输入:g 建立gpt分区表:
(2)建立分区
输入:n 添加一个分区

提示让输入开始扇区(一个扇区512B,按自己要分区容量大小进行计算)
输入2048,回车

让输入结束扇区,由于一个扇区512B,要创建200M的分区,应该输入:+200M;

(3)立第二个分区:
输入n;
回车
输入开始扇区: 回车 (默认开始扇区即可)
输入结束扇区:+200M

(4)建立第三个分区:
输入n;
回车
输入开始扇区:回车 (默认开始扇区即可)
输入结束扇区:直接回车(默认大那个数字)

(5)输入:w 保存并退出;
执行:lsblk 检验分区是否正确

格式化分区,并挂载

(1)格式化分区

执行:

1
# mkfs.fat -F32 /dev/sda

(格式化ESP分区)

1
# mkfs.ext4 /dev/sda2

(格式化boot分区)

1
# mkfs.ext4 /dev/sda3

(格式化根分区)

(2)挂载:

1
2
3
4
5
# mount /dev/sda3 /mnt
# mkdir /mnt/boot
# mount /dev/sda2 /mnt/boot
# mkdir /mnt/boot/EFI
# mount /dev/sda1 /mnt/boot/EFI

执行lsblk查看挂载是否正确

开始安装基本操作系统

执行:

1
# pacstrap -i /mnt base base-devel1

后开始安装

配置基础系统

(1)配置fstab

执行:

1
# genfstab -U /mnt >> /mnt/etc/fstab

最好再执行:

1
# cat /mnt/etc/fstab

检查一下
(2)切换到新系统

执行:

1
# arch-chroot /mnt /bin/bash

(3)进行本地语言设置

执行:

1
# nano /etc/locale.gen

反注释(删掉前面的#)
en_US.UTF-8 UTF-8
zh_CN.UTF-8 UTF-8
这两个,退出保存
执行:

1
# locale-gen

执行:

1
# echo LANG=en_US.UTF-8 > /etc/locale.conf

(4)设置时区

执行:

1
# ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

也可以执行:

1
# tzselect

按照提示选择时区
执行:

1
# hwclock --systohc --utc

设置硬件时间

引导系统

GRUB进行UEFI引导
执行:

1
# pacman -S dosfstools grub efibootmgr

安装引导工具

执行:

1
# grub-install --target=x86_64-efi --efi-directory=/boot/EFI --recheck

进行安装grub

执行:

1
# grub-mkconfig -o /boot/grub/grub.cfg

进行配置grub

用户管理

(1)设置root密码

执行:

1
# passwd

(2)添加用户

执行:

1
# useradd -m -g users -s /bin/bash 用户名

(务必添加一个 用户 ,否则后面sddm显示管理器登录的时候无法登录,sddm不会列出root用户)
执行:

1
# passwd 用户名

为刚才添加的用户设置密码
执行:

1
# nano /etc/sudoers

在 root ALL=(ALL) ALL 下面添加
用户名 ALL=(ALL) ALL
为你刚才创建的用户 添加sudo权限

(3)退出chroot重启

(笔记本请直接跳到下面网络配置,安装无线网络相关模块)
(也可以不重启,直接进行下面的网络配置和桌面环境配置)
执行:

1
# exit

退出chroot
执行:

1
2
# reboot
`

重启电脑

网络配置

开机进入电脑
(1)有线连接

1
# systemctl enable dhcpcd

root下执行不了此命令,可以省略,执行完下面的命令一会重启会自动启动dhcpcd服务)
启动dhcpcd

1
# systemctl start dhcpcd

开机自动启动dhcp服务

(2)无线连接:

1
# pacman -S iw wpa_supplicant dialog

(3)ADSL 宽带连接:

1
# pacman -S rp-pppoe# pppoe-setup # systemctl start adsl

(chroot下执行不了此命令)# systemctl enable adsl

按需定制化

安装桌面环境

(1)安装显卡驱动

确定显卡型号
执行:

1
# lspci | grep VGA

执行:

1
# pacman -S 驱动包

官方仓库提供的驱动包:

通用———————————-xf86-video-vesa
intel———————————-xf86-video-intel
Geforce7+————————–xf86-video-nouveau
Geforce6/7————————-xf86-video-304xx
hyperV ————xf86-video-fbdev

(2)安装X窗口系统

执行:

1
# pacman -S xorg

安装X窗口系统

执行:

1
# pacman -S xf86-input-synaptics1

(触摸板驱动,笔记版可装,台式机就不用了)执行,虚拟机不需要

1
# pacman -S ttf-dejavu wqy-microhei1

安装字体:Dejavu 和 微米黑字体(不安装的话 后面进入桌面环境设置系统语言为简体中文的时候会出现字体显示不全的问题)

(3)安装kde-plasma桌面环境

安装 Gnome桌面环境的直接跳到第(4)步
(kde和gnome桌面环境自带了大部分的驱动 ,安装其他桌面环境可能需要额外配置一些驱动,比如声卡)
想安装其他桌面环境 参照官方wiki
执行:

1
# pacman -S plasma

安装plasma

执行:

1
# pacman -S konsole

安装 kde下的控制台终端

执行:

1
# pacman -S dolphin

安装kde下的文件管理器
(可以直接执行:

1
# pacman -S kde-applications

安装kde套件,包含了常用的系统工具)
安装完后
执行:

1
# systemctl enable sddm

启用 sddm显示管理器

执行:

1
# systemctl enable NetworkManager

启用网络管理

执行:

1
# pacman -S plasma-nm

安装 网络管理的前端工具(图形界面)
执行:

1
# reboot

重启

进入系统后界面如下:

(4)安装Gnome桌面环境
执行:

1
# pacman -S gnome

安装gnome桌面
执行:

1
# pacman -S gnome-tweak-tool

安装gnome桌面优化工具
执行:

1
# pacman -S alacarte

安装gnome桌面菜单编辑器
执行:

1
# systemctl enable gdm

启用gnome窗口管理器服务
执行:

1
# systemctl enable NetworkManager

启用网络管理器服务
执行:

1
# reboot

13.安装完后的工作

(1)添加archlinuxcn源(里面包含了很多中国人常用而官方仓库又没有的软件)

执行:

1
# nano /etc/pacman.conf1

在 /etc/pacman.conf 文件末尾添加两行:

[archlinuxcn]
SigLevel=Never
Server = https://mirrors.ustc.edu.cn/archlinuxcn/$arch123

(2)安装中文输入法
执行:

1
# pacman -S fcitx-im fcitx-configtool

安装输入法引擎
(官方仓库里的输入法:
fcitx-cloudpinyin
fcitx-googlepinyin
fcitx-libpinyin
fcitx-sunpinyin)
执行:

1
# nano ~/.xprofile

添加一下内容

1
2
3
export GTK_IM_MODULE=fcitx
export QT_IM_MODULE=fcitx
export XMODIFIERS="@im=fcitx"12345

执行:

1
# pacman -S fcitx-sogoupinyin

安装搜狗输入法

(3)安装网易云音乐

执行:

1
# pacman -S netease-cloud-music

安装网易云音乐

(4)安装yaourt使用aur

执行:

1
# pacman -S yaourt

安装yarourt
以后可以使用yaourt 安装aur中的软件了 ,yaourt跟pacman使用方法一样
安装kde下的文件管理器
(5)安装浏览器
执行:

1
# pacman -S google-chrome

安装google浏览器(没法在线观看视频)
执行:

1
# pacman -S firefox1

安装火狐浏览器
(执行: # pacman -S flashplugin 安装flas插件,否则无法在线观看视频,chrome浏览器不支持flash)

(6)其他常用软件
可在链接里找
进行查找

评价模型的一些指标

发表于 2018-09-22

衡量模型测试结果的指标

在衡量训练好的模型预测结果有很多,如准确率、精确率、召回率等等。刚接触这些概念的时候,总是有点懵,因此留下此文,以备他日查看。

首先介绍一些概念(参考维基百科):


准确率与精确率

准确率(accuracy)是指被测量的测得值与其真值的一致程度;测量准确度有时被理解为赋予被测量的测得值之间的一致程度。
精确率(precision)测量结果与被测量(约定)真值的一致程度。

好像走错片场了,这是测量的概念,在上科学仪器时学的,这里回顾一下

P1

精确率与召回率

precision (also called positive predictive value) is the fraction of relevant instances among the retrieved instances,

recall (also known as sensitivity) is the fraction of relevant instances that have been retrieved over the total amount of relevant instances.

P2

以上图为例
精确率为:
$$ P = TP/(TP+FN) $$
精确率是指预测正确的正类与预测的全部正类之比

召回率为:
$$ R = TP/(TP+FP) $$
召回率,就是预测正确的正类与实际正类总数之比,FN是没预测出来的正类。
医院要求召回率要很高,假设确诊癌症为positive,本来是癌症却没诊断出癌症,那医术实在不敢恭维。

但常常精确率与召回率是互斥的,提高了精确率,召回率就低了,因此提出了F1 score
$$ F1 = 2PR/(P+R) $$

是不是很熟悉,像算电阻并联后的总电阻值,并联的电阻值小于任何一个电阻的值,但是两个一样的电阻,并联后只有一半大小,为了归一化,乘了一个2。F1值综合了P与R,只有都大时,F1才更大,单纯提高一个,效果可能不升反降。

12

Supo Zhang

I'm A man who constantly to pursue self-breakthrough and not satisfy current situation

15 日志
8 标签
© 2019 Supo Zhang
由 Hexo 强力驱动 v3.7.1
|
主题 – NexT.Gemini v6.4.1