C/C++ Development In Visual Studio Code

由于我工作环境是Linux和Mac,个人的工作目录和开发环境一直来回切换,之前一直使用emacs,emacs非常强大和可定制化。最近看到vscode的社区不断壮大,在好基友的推荐下,试用vscode,发现非常不错。于是记录和分享到博客中。今天主要给大家讲解的vscode配置c/c++ ide开发环境,当然官网支持很多种可定制化的配置。

开发环境搭建

我们首先安装vscode,官网是:vscode。我们下面来安装支持c/c++开发环境的安装包。 * cpptools * cmake * C/C++ Clang

我们在工作目录依次安装如下,c_cpp_properties.json(指定c/c++包和平台相关的配置文件),launch.json(debug 调试),tasks.json(你的编译等命令)

机器学习性能度量

我们在进行机器学习时需要衡量机器学习的优劣和本身模型的准确程度,比如简单的衡量数据的准确率和错误率,但是我们更关心的是模型的泛化能力的指标,即基于模型的所选的item相关性以及模型分类指标的好坏。

机器学习度量

  • error rate(错误率):把分类错误的样本数占样本总数的比例。$$E=a/m$$
  • accuracy(精确度):分类正确的样本数占样本总数的比例。$$acc=1-E$$
  • training error(训练误差):学习器在训练集上的误差。
  • generalization error(泛华误差):在新样本上的误差。

在机器学习中由很多机器学习算法,那么如何选择这些算法和模型,如何评估这些算法和模型,评估模型的算法和准确率的方式很多,我们依次来介绍一下几个常用的指标。TP、TN、FP、FN、Acc、Recall、F1、ROC和ROI

各种机器学习衡量指标

Accuracy、Precision、Recall和F1-Score

混淆矩阵是按照预测分类和实际分类的类别对比,如下图所示: 混淆矩阵 如上表所示,行表示预测的label值,列表示真实label值。TP,FP,FN,TN分别表示如下意思: * TP(true positive):表示样本的真实类别为正,最后预测得到的结果也为正 * FP(false positive):表示样本的真实类别为负,最后预测得到的结果却为正 * FN(false negative):表示样本的真实类别为正,最后预测得到的结果却为负 * TN(true negative):表示样本的真实类别为负,最后预测得到的结果也为负

$$ Accuracy = \frac{TP+TN}{TP+FP+TN+FN} $$ $$ Precision = \frac{TP}{TP+FP} $$ $$ Recall = \frac{TP}{TP+FN} $$ $$ SP = \frac{TN}{TN + FP}
$$

  • Accuracy:表示预测结果的精确度,预测正确的样本数除以总样本数。
  • Precision,准确率,表示预测结果中,预测为正样本的样本中,正确预测为正样本的概率;
  • Recall,召回率,表示在原始样本的正样本中,最后被正确预测为正样本的概率;
  • Specificity,常常称作特异性,它研究的样本集是原始样本中的负样本,表示的是在这些负样本中最后被正确预测为负样本的概率。

在实际当中,我们往往希望得到的precision和recall都比较高,比如当FN和FP等于0的时候,他们的值都等于1。但是,它们往往在某种情况下是互斥的。例如,有50个正样本,50个负样本,结果全部预测为正样本,那么TP=50,FP=50,TN=0,FN=0,按照上面的公式计算,可以得到正样本的recall却为1,precision却为0.5.所以需要一种折衷的方式,因此就有了F1-score。

$$ F1-score = \frac{2\times recall \times precision}{ recall + precision} $$

F1-score表示的是precision和recall的调和平均评估指标. 另外还有一个指标,即MCC,该指标对于不均衡数据集的评估非常有效,公式如下:

$$ MCC=\frac{(TP \times TN)-(FP \times FN)}{\sqrt{(TP+FP)\times(TP+FN)\times(TN+FP)\times(TN+FN)}} $$

结构化的机器学习流程

机器学习可以通过结构化的流程来梳理:1.定义问题和需求分析->2.数据探索->3.数据准备->4.评估算法->5.优化模型->6.部署。 * 导入类库 * 导入数据集 * 数据统计分析 * 数据可视化 * 数据清洗 * 特征选择 * 数据转换 * 分离数据集 * 定义模型评估标准 * 算法审查 * 算法比较 * 算法调参 * 集成算法 * 预测评估数据集 * 利用数据生成模型 * 序列化模型

数据理解

数据的理解主要在于分析数据维度、数据类型属性、数据分布以及相关性等。数据属性的相关性是指数据的两个属性的互相影响。

数据统计分析

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 数据导入与数据维度探索
from numpy import loadtxt
with open(filename,'rt') as rawdata:
    data = loadtxt(rawdata,delimiter=',')
    print(data.shape)
#数据属性和描述性统计
print(data.dtype)
print(data.describe())
#数据根据类别分类
print(data.group("class").size())
#数据属性相关性(皮尔逊相关系数,1完全正相关,-1完全负相关,0不相关)
print(data.corr(method="pearson"))
#数据分布分析,skew代表高斯分布的偏离程度,数据接近于0表示数据偏差非常小
print(data.skew())

熵的理解

熵在信息论中代表随机变量不确定度的度量。一个离散型随机变量X的熵H(X)定义为: $$ H(X)=- \sum_{x \in X} p(x) \cdot logp(x) $$ 明确定义的科学名词且与内容无关,而且不随信息的具体表达式的变化而变化。是独立于形式,反映了信息表达式中统计方面的性质。是统计学上的抽象概念。信息熵的一种解释是,它表示的是最短的平均编码长度。同样的,不确定性越大,熵就越大。信息熵的单位是比特(bit)。我们举两个简单的例子: 第一个例子: 32支球队,在无任何先验信息的前提下,用二分法猜冠军队伍,最多猜5次,即: $$ log\frac{1}{32}=5log\frac{1}{32}=5bit $$ 第二个例子:赌马比赛里,有4匹马A,B,C,D,获胜概率分别为 $$ \frac{1}{2},\frac{1}{4},\frac{1}{8},\frac{1}{8} $$

  • 如果 X=A ,那么需要问1次(问题1:是不是A?),概率为 $$ \frac{1}{2} $$
  • 如果 X=B ,那么需要问2次(问题1:是不是A?问题2:是不是B?),概率为 $$ \frac{1}{4}$$
  • 如果 X=C ,那么需要问3次(问题1,问题2,问题3),概率为
    $$ \frac{1}{8}$$
  • 如果 X=D ,那么同样需要问3次(问题1,问题2,问题3),概率为 $$ \frac{1}{8}$$

通过熵的定义得到

$$ H(x)=-\sum_{x \in {A,B,C,D}}p(x)\cdot log p(x)=\frac{1}{2} \cdot log2+\frac{1}{4} \cdot log4+\frac{1}{8} \cdot log8+\frac{1}{8} \cdot log8=1.75 $$

在二进制计算机中,一个比特为0或1(代表二元问题)。在计算机中,我们给哪一匹马夺冠这个事件进行编码,所需要的平均码长为1.75个比特。其定义为:

$$ L(C)=\sum_{x \in X}p(x)l(x) $$

为了尽可能减少码长,我们要给发生概率p(x)较大的事件,分配较短的码长l(x)。所以那么 $${A,B,C,D}$$ 四个实践,可以分别由 $${0,10,110,111}$$ 表示. (1)信息熵只反映内容的随机性,与内容本身无关。不管是什么样内容的文件,只要服从同样的概率分布,就会计算得到同样的信息熵。 (2)信息熵越大,表示占用的二进制位越长,因此就可以表达更多的符号。所以,人们有时也说,信息熵越大,表示信息量越大。不过,由于第一点的原因,这种说法很容易产生误导。较大的信息熵,只表示可能出现的符号较多,并不意味着你可以从中得到更多的信息。 (3)信息熵与热力学的熵,基本无关。

相对熵(KL离散度)

相对熵又叫做KL离散度,其定义为: $$ KL(f(x)||g(x))=-\sum_{x \in X} f(x) \cdot log\frac{f(x)}{g(x)} $$ KL 散度是两个概率分布f(x)和g(x)差别的非对称性的度量。KL散度是用来度量使用基于f(x)的编码来编码来自g(x)的样本平均所需的额外的位元数。 很容易证明,有三个结论: (1) 两函数完全相同时,KL=0 (2) KL越大,差异越大 (3) 对概率分布或者概率密度函数(>0), KL可用来衡量两个随机变量分布的差异性。

交叉熵

对一随机事件,其真实概率分布为p(i),从数据中得到的概率分布为q(i),则我们定义,交叉熵为: $$ H(p,q)=\displaystyle\sum _{x}p(x)log\frac{1}{q(x)}=-\sum _{x}p(x)logq(x) $$

核心理解: * 用p来衡量识别一个样本的信息量即最小编码长度(信息熵)。

$$ H(p)=\sum p \cdot log\frac{1}{p} $$

  • q来估计真实分布为p的样本的信息量.

$$ H(q,p)=\sum p \cdot \frac{1}{q} $$

  • 则估算多出来的冗余信息量.

$$ D(p||q)=H(p,q)-H(p)=\sum p \cdot log\frac{p}{q}(KL离散度) $$

在机器学习中,p通常设定为真实标记的分布,q设定为训练后模型预测标记的分布:

$$ H(p,q)=H(p)+D_{KL}(pq) $$

即:交叉熵=信息熵+KL散度(相对熵) 由于信息熵H(p)H(p)是固定不变的,因此我们在机器学习中就用交叉熵作为损失函数。常见的做法是先用Softmax函数将神经网络的结果转换为概率分布,然后用交叉熵刻画估算的概率分布与真实的概率分布的"距离"。

Python进阶系列三

Python 进阶

我们在Python进阶教程(二),介绍了一些Python进阶用法。今天给大家介绍的是和c/c++混合编程的用法。我们都知道特别是Python2.x版本GIL的影响造成多线程计算方面是鸡肋,并且常常在高性能计算方面依赖很多包。如果我们想切合我们自己的业务处理,比如高性能部分我们用C/C++生成动态链接库,Python调用该动态链接库。我们今天主要介绍Python在和c/c++混合编程。

Python和C/C++混合编程

在和C/C++代码进行混合编程时,我们通常需要采用以下几个技术点: * Ctypes:C++编译成动态链接库(DLL,即运行时动态调用),然后通过ctypes来解析和加载动态链接库,python调用该动态链接库。从ctypes的文档中可以推断,在各个平台上均使用了对应平台动态加载动态链接库的方法,并通过一套类型映射的方式将Python与二进制动态链接库相连接。ctypes 实现了一系列的类型转换方法,Python的数据类型会包装或直接推算为C类型,作为函数的调用参数;函数的返回值也经过一系列的包装成为Python类型。 优点: 1.Python内建,不需要单独安装 2.可以直接调用二进制的动态链接库 3.在Python一侧,不需要了解Python内部的工作方式 4.在C/C++一侧,也不需要了解Python内部的工作方式 5.对基本类型的相互映射有良好的支持 缺点: 1.平台兼容性差 2.不能够直接调用动态链接库中未经导出的函数或变量 3.对C++的支持差 * SWIG:通过提供的接口文件来调用。 * Python/C在C中直接扩展Python的代码。

Python进阶系列二

概述

在上一篇博客中,我们介绍了Python进阶系列一,还有一些新的技巧没有翻译完,我们下面来继续我们的翻译。

Intermediate Python 中译(二)

Decorators

Decorators装饰器是Pytho非常重要的特性,相当于Java注解功能。装饰器是修饰函数的一种语法特性,可以使其功能发生一些改变。在Python函数也是第一等公民,可以直接赋值对象和使用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def hi(name="brian"):
    return "hi,"+name
print(hi())
#输出
hi,brian
greet = hi
print(greet())
#输出
hi,brian
#此时已经删除hi对象,无法运行hi()方法,但是可以通过greet()方法运行
del hi
print(greet())
#输出
hibrian

我们也可以在函数内定义函数,比如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
def hi(name="brian"):
    print("now you are inside in the hi() method")
    def greet():
        return "now you are in inside the greet() method"
    def welcome():
        return "now you are in inside the welcome() method"
    print(greet())
    print(welcome())
    print("now you are back in the hi() method")
hi()
#输出为
now you are inside in the hi() method
now you are in inside the greet() method
now you are in inside the welcome() method
now you are back in the hi() method

我们可以看到在函数(主函数)内定义函数(子函数),只要是不调用子函数,子函数只是声明并不执行,当执行完子函数时又返回了主函数。由于Python只是函数第一等公民的功能特性,那么在Python代码中是可以将Python的函数可以作为变量使用并将其作为返回值亦可。从主函数中返回子函数(即从函数中返回函数)

Python进阶系列一

概述

hi,朋友们大家好,今天将英文原著作者 @yasoob《Intermediate Python》进行翻译和在工作中使用的Python技巧进行了总结。Gitbook里面有翻译的版本,大家可以下载下来看看。我今天主要是将该英文原著翻译成适合自己的理解的语言,并附加一些自己在工作中使用Python的技巧。废话少说,下面我们依次来学习一下@yasoob的原著。

Intermediate Python 中译

如果在翻译过程中有问题或者code无法运行,还请各位大侠指正。

*args和**kwargs

我们在函数定义时,会经常使用*args和**kwargs这两个魔法变量,特别是函数参数数量不确定或参数较多时。当然*args和**kwargs这两个name并不是惟一的name,可以改成*as和**kwas,只要在命名前面加上修饰符*和**即可。我们先来看一下*args。

*args

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
def test_args_kwargs(name,*args):
    print("frist arg: ",name)
    for arg in args:
        print("another arg through *argv: ",arg)

test_args_kwargs("brian","eric","rose")
#输出为
frist arg:  brian
another arg through *argv:  eric
another arg through *argv:  rose

我们也可以通过参数列表进行传递,比如:

1
2
3
4
5
names=("brian","eric","rose")
test_args_kwargs(*names)
frist arg:  brian
another arg through *argv:  eric
another arg through *argv:  rose

**kwargs

将不定长度的键值对作为参数传递给函数,我们来看一下。

1
2
3
4
5
6
7
8
9
def greet_me(**args):
    for key,value in args.items():
        print("{0}=={1}".format(key,value))

greet_me(name="brian",age=20,sex="男")
#输出为
name==brian
age==20
sex==

或者也可以这样

1
2
3
4
5
6
person = {"name":"brian","age":20,"sex":"男"}
greet_me(**person)
#输出为
name==brian
age==20
sex==

如何使用

在很多场景里面都大量使用比如: 1.装饰器 2.函数定义 3.单元测试 4.猴子补丁(在运行时修改某些代码),在英文版本里面主要介绍了通过猴子补丁修改API来进行测试。 ......

1
2
3
4
5
import someclass

def get_info(self,*args):
	return "Test Data"
someclass.get_info=get_info

Python 性能分析

Python性能分析与优化

一个优秀的程序员,在保证业务正常的条件下都会追求自己的程序更快、更省。更快:运行时间短;更省:相对节省计算机资源(比如:CPU、Memory)。一般都是以这两种衡量方式来度量自己的程序及进一步优化自己程序的空间。更专业的性能分析软件一般有两类方法论:event-based profiling和statistical profiling

Event-based Profiling

并不是所有编程语言都支持这类性能分析,支持这类分析的语言主要有:

  • Java:JVMTI(JVM Tools Interface,JVM工具接口)为性能提供了钩子,可以跟踪函数调用和事件执行等等。
  • .NET :也提供了事件监听器,主要得益于.net 运行时。
  • Python:可以利用sys.setprofile函数来跟踪函数python(call,return,exception)或者c(call,return,exception).

基于事件的性能分析(event-based profiler or tracing profiler)是通过手机程序执行过程中的具体事件进行工作的,这些性能分析会产生大量的数据,基本而言,你监听的事件越多产生的数据量句越多。这导致它们不太实用,在开始对程序进行性能往往不是首选,当其他性能分析不够用或者不精准它们可以作为最后的选择。

Python GIL

概述

GIL(Global Interpreter Lock)是什么东东?为什么当一些Pythoners在开发一些多线程操作的时候,都会有些很多疑问?多线程真的很糟糕吗?我该如何实现多线程并发操作?今天博主带你详细的介绍一下GIL。

GIL原理

由于Python是动态解释性语言,即解释运行。运行Python代码时都会通过Python解释器解释执行,Python官方默认的解释器是Cython,当然你也可以选择自己的Python解释器(PyPy,JPython),其中JPython就没有GIL的限制。在解释器解释执行任何Python代码时,首先都需要they acquire GIL when running,release GIL when blocking for I/O。如果没有涉及I/O操作,只是CPU密集型操作或者,解释器会每隔100 ticks(低级的解释器指令)就释放GIL(通过 sys.setcheckinterval来修改)。GIL是实现Python解释器(Cython)时所引入的一个概念。GIL不是Python的特性。

线程执行模型

我们先看一下Python下多任务线程执行模型,下面的图取自David Beazley大神,并且在他的个人网站中对GIL进行深度的解剖。如果想了解更深入的东西,可以去逛逛他的网站。

Linux Install Jupyter

概述

由于我的开发环境是CentOS,现在主要是以Python为主,偶尔夹杂C/C++和GoLang语言。在本地开发、测试、提交git和在生产环境再去测试、开发,并且会在生产环境会有临时性的开发任务和调整,都是比较浪费时间的。不建议直接修改生产环境的代码,临时性的修改后一定要测试并稳定运行一段时间。 写这篇博客主要是为了利用线上资源,实现跨平台开发和测试代码示例用的。所以就在服务器安装了IPython,Jupyter和Notebook

安装和配置

在安装IPython,Jupyter和Notebook时,不仅需要安装Python和Pip,而且还需要安装一些开发操作系统的工具集比如('Development Tools‘)。废话少说,直接写代码。

基本包安装

1
2
3
4
yum update -y
yum install python-pip -y
yum install bzip2 -y
yum groupinstall "Development Tools" -y

安装完pip之后,最好把pip源改为国内源,修改如下:

1
2
3
4
5
6
7
mkdir ~/.pip
cat > ~/.pip/pip.conf << EOF
[global]
index-url = http://mirrors.aliyun.com/pypi/simple/
[install]
trusted-host=mirrors.aliyun.com
EOF