r 语言初学者指南
My job focuses almost exclusively on NLP. So I work with text. A lot.
我的工作几乎完全专注于NLP 。 所以我处理文本。 很多。
Text ML comes with its own challenges and tools. But one recurring problem is excessive dimensionality.
Text ML带有自己的挑战和工具。 但是一个反复出现的问题是尺寸过大。
In real life (not Kaggle), we often have a limited number of annotated examples on which to train models. And each example often contains a large amount of text.
在现实生活中(不是Kaggle),我们经常会在有限的带注释的示例中训练模型。 每个示例通常包含大量文本。
em算法和gmm算法
GMM is a really popular clustering method you should know as a data scientist. K-means clustering is also a part of GMM. GMM can overcome the limitation of k-means clustering. In this post, I will explain how GMM works and how it will be trained.
GMM是一种非常流行的群集方法,您应该作为数据科学家知道。 K均值聚类也是GMM的一部分。 GMM可以克服k均值聚类的局限性。 在这篇文章中,我将解释GMM的工作原理以及培训方法。
混合模型 (Mixture Model) What is the mixture model? We can think of just the combination of the models. If you combine the Gaussian Distribution with weights, then it will be the Gaussian Mixture Model.
表中的内容(Table of Content) Introduction
介绍What are Window (Analytic) Functions?
什么是窗口(分析)功能?Anatomy of a Window Function
窗函数剖析 Example of an Aggregate Function vs Window Function
集合函数与窗口函数的示例Advantages of Window Functions
窗口功能的优点 介绍(Introduction) You’re comfortable with simple queries (SELECT FROM WHERE GROUP BY) and you want to take your skills to the next level. What should you learn next?
您对简单的查询(从WHERE GROUP BY中选择)感到满意,并且希望将自己的技能提高到一个新的水平。 接下来您应该学什么?
Window functions, also called analytics functions, are powerful SQL functions that will take you from a beginner SQL user to an intermediate SQL user.
UUID是什么 ? UUID 是指Universally Unique Identifier,翻译为中文是通用唯一识别码,UUID 的目的是让分布式系统中的所有元素都能有唯一的识别信息。如此一来,每个人都可以创建不与其它人冲突的 UUID,就不需考虑数据库创建时的名称重复问题。
定义
UUID 是由一组32位数的16进制数字所构成,是故 UUID 理论上的总数为1632=2128,约等于3.4 x 10123。
也就是说若每纳秒产生1百万个 UUID,要花100亿年才会将所有 UUID 用完
格式
UUID 的十六个八位字节被表示为 32个十六进制数字,以连字号分隔的五组来显示,形式为 8-4-4-4-12,总共有 36个字符(即三十二个英数字母和四个连字号)。例如:
123e4567-e89b-12d3-a456-426655440000 xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx 数字 M的四位表示 UUID 版本,当前规范有5个版本,M可选值为1, 2, 3, 4, 5 ;
数字 N的一至四个最高有效位表示 UUID 变体( variant ),有固定的两位10xx因此只可能取值8, 9, a, b
UUID版本通过M表示,当前规范有5个版本,M可选值为1, 2, 3, 4, 5。这5个版本使用不同算法,利用不同的信息来产生UUID,各版本有各自优势,适用于不同情景。具体使用的信息
version 1, date-time & MAC addressversion 2, date-time & group/user idversion 3, MD5 hash & namespaceversion 4, pseudo-random numberversion 5, SHA-1 hash & namespace 使用较多的是版本1和版本4,其中版本1使用当前时间戳和MAC地址信息。版本4使用(伪)随机数信息,128bit中,除去版本确定的4bit和variant确定的2bit,其它122bit全部由(伪)随机数信息确定。
问题描述:
mybatis-plus查询时selectById方法查询结果出问题:
执行的sql如下:
解决方案:
这是因为mybatis-plus代码生成时,实体没加@TableId注解,加上即可
文章目录 1. NVIDIA-SMI介绍2. NVIDIA-SMI命令系列详解2.1 nvidia-smi2.2 nvidia-smi -q2.3 设备修改选项2.4 nvidia-smi dmon2.5 nvidia-smi pmon 1. NVIDIA-SMI介绍 nvidia-smi简称NVSMI,提供监控GPU使用情况和更改GPU状态的功能,是一个跨平台工具,它支持所有标准的NVIDIA驱动程序支持的Linux发行版以及从WindowsServer 2008 R2开始的64位的系统。该工具是N卡驱动附带的,只要安装好驱动后就会有它。
Windows下程序位置:C:\Program Files\NVIDIACorporation\NVSMI\nvidia-smi.exe。Linux下程序位置:/usr/bin/nvidia-smi,由于所在位置已经加入PATH路径,可直接输入nvidia-smi运行。
2. NVIDIA-SMI命令系列详解 2.1 nvidia-smi 显示所有GPU的当前信息状态
显示的表格中:
Fan: 风扇转速(0%–100%),N/A表示没有风扇
Temp: GPU温度(GPU温度过高会导致GPU频率下降)
Perf: 性能状态,从P0(最大性能)到P12(最小性能)
Pwr: GPU功耗
Persistence-M: 持续模式的状态(持续模式耗能大,但在新的GPU应用启动时花费时间更少)
Bus-Id: GPU总线,domain: bus: device.function
Disp.A: Display Active,表示GPU的显示是否初始化
Memory-Usage: 显存使用率
Volatile GPU-Util:GPU使用率
ECC: 是否开启错误检查和纠正技术,0/DISABLED, 1/ENABLED
Compute M.: 计算模式,0/DEFAULT,1/EXCLUSIVE_PROCESS,2/PROHIBITED
附加选项:
nvidia-smi –i xxx
指定某个GPU
nvidia-smi –l xxx
动态刷新信息(默认5s刷新一次),按Ctrl+C停止,可指定刷新频率,以秒为单位
nvidia-smi –f xxx
将查询的信息输出到具体的文件中,不在终端显示
2.2 nvidia-smi -q 查询所有GPU的当前详细信息
官方文档:https://www.paddlepaddle.org.cn/documentation/docs/zh/api_cn/layers_cn/clip_cn.html
示例:
import paddle.fluid as fluid import numpy as np input = fluid.data( name='data', shape=[-1, 5], dtype='float32') reward = fluid.layers.clip(x=input, min=0.5, max=0.9) place = fluid.CPUPlace() exe = fluid.Executor(place) exe.run(fluid.default_startup_program()) np_x = np.random.random(size=(3, 5)).astype('float32') print("np value: \n", np_x) output = exe.run(feed={"data": np_x}, fetch_list = [reward]) print() print("output: \n", output) 结果:
np value: [[0.4650584 0.8003481 0.9856342 0.86992013 0.19400549] [0.18352304 0.7047317 0.7592452 0.83213526 0.57000315] [0.53873336 0.84840363 0.7514601 0.13381955 0.03195545]] output: [array([[0.5 , 0.
文章目录 13.5智能指针13.5.1 raw指针和智能指针13.5.2 unique_ptr 13.5智能指针 13.5.1 raw指针和智能指针 new运算符分配一块内存,可将这块内存的地址保存在一个指针变量 指针变量不但可以指向动态分配的内存,也可以指向一个自动变量: 一个T* 类型的指针变量可存储动态分配的内存地址, 也可以存储一个程序块的普通变量的地址。这种指针称为raw指针(原始指针),因为除了内存地址外它没有存储别的东西。 raw指针指向的动态分配内存应该及时释放,并且对new分配的内存要用delete释放new[]分配的内存要用 delete[]释放。如果一个指针变量在指向新的内存时,没有释放原先指向的动态内存,就会造memory leaks上述代码中p开始指向的是new分配的动态内存,然后修改p指向了变量i,而原先p指向的内存并没有用 delete释放, 这块动态内存就一直被程序占用,程序的其他部分或其他程序没法访问或释放这块内存,造成内存泄漏。 如果用错了 delete也会造成内存汇漏 13.5.2 unique_ptr std::unique_ptr<>对象是对raw指针的包裏 将一个T*类型的动态分配内存块的raw指针作为构造函数的参数就可以创建一个std:: unique_ptr<T>对象,它退出其作用域销毁时, 析构函数自动释放其包含的raw指针指向的动态内存 我的天,那这个对象析构的时候他指向的东西也被释放了,我草!!
std::unique_ptr<double>类对象p包裹动态分配的内存f()结束时,p被销毁,其析构函数会自动释放动态分配的内存。p和raw指针样, 用解引用运算符*(或间接访问运算符)访问p指向的动态内存 作为一个类对象,p可用unique_ptr<>类的成员函数。用get()方法得到其包裹的raw指针: reset()将一个新的raw指针传递给它,原来的raw指针指向的内存被自动释放
一、get请求导出文件 1、window.open(url) 会自动打开一个标签页,下载完后会自动关闭这个标签页。
2、window.location(url) 或 location.href = url 在当前标签页下载。
3、< a href=“url” download=“filename”>点击链接下载< /a> 在当前标签页下载。
总结 优点:代码简单。
缺点:当导出失败时,返回的错误信息会覆盖原来的页面,错误信息可读性差。
二、post请求导出文件 1、思路 通过判断返回值的header.‘content-type’ 是否是 application/octet-stream,来确定文件导出是否成功!
content-type:
application/octet-stream: 返回值类型为二进制流,即导出成功;application/json;charset=utf-8:返回值类型为json格式,即导出失败信息。由于我们请求的时候设置了responseType=‘blob’,所以返回值会被转换成二进制流。 2、实现 // 以axios为例 axios({ url, method: 'post', responseType: 'blob', params }) .then(async (res) => { const { data, status, statusMsg, headers: {'content-type': contentType} } = res if (status === 200) { // 导出失败,返回错误信息 if (contentType !== 'application/octet-stream') { // 将返回的blob格式的返回值,转成对象 const responseMessage = JSON.
初次在react项目中使用element框架,碰到了一些很简单的问题,但是卡了很久才解决,写篇博客记录一下。
1、安装: 在项目目录下下载:
npm i element-react --save
npm install element-theme-default --save
注:element官网快速上手中有提到
2、全局引用 在项目跟录下的index.js中全局引入
3、在项目中引用你需要的element组件 可能会报错
解决方式:在项目中下载依赖npm install react-hot-loader@next --save即可
4、引入组件可能还会报错 例如如下我引入的是走马灯
如上报错,解决方式,引入Carousel即可
如果引用其他组件也报错,解决方式如上
一:手动配置mac地址表(静态方式)
(1)以太网帧结构:
(2)配置了vlan后的以太网帧结构(多了4个字节的vlan tag)
含义:
为什么要划分vlan?
答:有效的控制了广播域的范围,比如,在通常情况下,主机发出的报文是不带VLAN tag的(即Untagged 报文),那么这样的报文是如何划分到某个vlan种去的?划分vlan有多种方式,比如端口划分,协议,IP,mac等等。这里一般是采用端口划分。
比如当一个untagged报文进入端口时,就会被加上VLAN tag,后面会根据进入端口的untagged报文的mac地址和端口所属vlan id进行后续转发。
好处:
(a)有效控制广播域范围,广播流量仅在VLAN内转发;
(b) 配置灵活,虚拟局域网的范围可根据需要随时调整;
© 由于不同VLAN之间二层隔离,带来了更高的安全性。
一、QPSK原理 QPSK规定了四种载波相位,分别为45°,135°,225°,315°,调制器输入的数据是二进制数字序列,为了能和四进制的载波相位配合起来,则需要把二进制数据变换为四进制数据,这就是说需要把二进制数字序列中每两个比特分成一组,共有四种组合,即00,01,10,11,其中每一组称为双比特码元。每一个双比特码元是由两位二进制信息比特组成,它们分别代表四进制四个符号中的一个符号。
双比特与载波相位的关系如表所示:
二、QPSK调制方法 QPSK信号产生的方法有两种,一种是相位选择法,如下图所示:
另一种是相乘电路,下面详细介绍:
下图中输入的基带信号an是二进制不归零双极性码元,它被串并变换电路变成两路码元I(t)和Q(t)。[转换规则可以设定为奇数位为I(t),偶数位为Q(t)。例:1011001001 I(t):11010 Q(t):01001]变成并行码元I(t)和Q(t)后,其每个码元的持续时间是输入码元的2倍,这两路并行码元序列分别用以和两路正交载波相乘。在相加电路中相加后得到输出矢量s(t)。
三、QPSK的时域波形 对于序列01 01 10 00 11 01 00,其按A、B方式调制的时域波形如下图所示:
四、QPSK的功率谱 由于QPSK信号可被理解为由同相支路和正交支路两路2PSK叠加而成,但是载波的幅度与码元速率不同,因此QPSK信号的功率谱密度相当于2PSK信号的功率谱密度的线性叠加:
因此QPSK信号的功率谱示意图如下:
由上图可以看出其带宽等于基带码元速率,峰值频率为等于载波频率。
五、星座图的基本概念 在数字通信领域中,经常将数字信号在复平面上表示,以直观的表示信号以及信号之间的关系,这种图示就是星座图。星座图可以看成数字信号的一个“二维眼图”阵列,同时符号在图中所处的位置具有合理的限制或判决边界。如果要将数字信号发送出去,一般不会直接发0或1,而是先将0,1信号(bit)按照一个或者几个组成一组,比如每两个bit组成一组,即有00,01,10,11,总共四种状态,此时可以选择QPSK调制,QPSK四个点组成一个QPSK的星座图,每个点与相邻的点相差90度(幅度是相同的),一个星座点对应一个调制符号。星座图的作用主要是在调制时用于映射,而接收时用于判断发送的到底是哪个点,从而正确解调数据。
六、MATLAB实现QPSK已调信号生成的思路 按照QPSK调制原理,将信源进行串/并转换,分别即取出信源(m序列)的奇偶位,并将码速变为原来的一半,接着将该单极性非归零信号变为双极性非归零信号。对该两路信号进行采样,对奇数列采样序列乘cos(2πf_c t)减去偶数列采样序列乘sin(2πf_c t),得到QPSK信号。对QPSK信号进行fft再进行fftshift求其频谱。将基带信号两位两位组合,映射到QPSK星座图,画图得到信源星座图。
import pandas as pd from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split import matplotlib.pyplot as plt import plotly.graph_objs as go from plotly.offline import init_notebook_mode, iplot init_notebook_mode(connected = True) from sklearn import metrics df = pd.read_csv(filepath_or_buffer='./data/iris/iris.csv',header=None) df.columns = ['sepal_len', 'sepal_width', 'petal_len', 'petal_width', 'class'] df.tail() sepal_lensepal_widthpetal_lenpetal_widthclass1456.73.05.22.3Iris-virginica1466.32.55.01.9Iris-virginica1476.53.05.22.0Iris-virginica1486.23.45.42.3Iris-virginica1495.93.05.11.8Iris-virginica df.loc[df['class'] == 'Iris-setosa','class'] = 0 df.loc[df['class'] == 'Iris-versicolor','class'] = 1 df.loc[df['class'] == 'Iris-virginica','class'] = 2 df.tail() sepal_lensepal_widthpetal_lenpetal_widthclass1456.73.05.22.321466.32.55.01.921476.53.05.22.021486.23.45.42.321495.93.05.11.82 df.head() sepal_lensepal_widthpetal_lenpetal_widthclass05.13.51.40.2014.93.01.40.2024.73.21.30.2034.63.11.50.2045.03.61.40.20 labels = df.groupby('class').size().index values = df.groupby('class').size() trace = go.
从九月份开始,我们进行了为期一个月的pocib外贸业务实训。Pocib就是国际贸易从业技能综合实训,POCIB项目重点针对国际贸易及相关专业的高校学生以及国际贸易行业新进人员,以提高学习者的外贸综合业务技能为目标,以仿真的在线国际贸易游戏为核心方式,是一种创新、高效、务实和科学的教学方法和培训手段。通过为期一个月的模拟业务实训,我收获了很多。
一、 实训过程
Pocib中一共有10个国家,只有不同的国家才能进行贸易往来。通过查阅pocib百科,我了解到一个完整的进出口业务需要经历四大阶段:(1)交易准备阶段。(2)交易磋商阶段。(3)签订合同阶段。(4)履行合同阶段。
首先,我们的任务就是为交易做好准备。下载安装好pocib软件后,需要登录注册公司。具体注册填写公司各项信息可以按照pocib百科当中的要求进行填写。在填写公司介绍的时候无需固定经营范围,因为在pocib当中,贸易商可以出口国内工厂中的所有商品。需要注意的是注册之后资料不能再修改,所以填写的时候一定要认真,检查无误后再点击完成注册。公司注册完毕之后,就可以发布广告了。发布广告可以帮助我们更好地销售产品,让其余公司关注到自己公司的产品。这里需要注意两点,第一点就是发布广告和市场信息都要收取一定的费用,会从我们的初始资金当中直接扣除,因为我不小心点了两次发布广告信息,所以扣了我两次的费用。第二点需要注意的是,不论是广告还是市场信息,都是有时效性的,一旦过期,就不会在市场中显示,需要重新发布。当然,如果你和你的同学做贸易,彼此清楚交易的商品,可以省略掉广告发布这一步,这样可以节约一定的资金。
在准备工作完成之后,就要进行交易磋商了。我是古巴国的公司,我的第一笔业务是和巴西公司做的出口业务,出口柑橘给巴西。第二步的任务就是进行交易磋商了。需要进口商或出口商发送建立业务关系的邮件,收到回复如果无误后,就可以进行发盘和询盘操作了。作为出口商,我需要填写出口预算表,进口商需要填写进口预算表,核算成本与利润,我认为报价核算是比较难的一部分,核算一定要认真,否则可能会导致利润过高或利润过低亏本。根据我们选择的贸易术语的不同,报价核算也有所不同。在做第一笔的时候,我不知道从何处入手,通过查阅pocib百科,可以找到核算方法,按照给的示例去进行核算,就简单多了。核算的过程一定要认真仔细,因为预算的准确度会影响到我们的的成绩评定。如果进口商可以接受发盘的话,收到进口商回复后,我们就可以进行下一阶段了。
第一笔交易中,我是出口商。合同是由出口商来拟定的,我在填写合同的过程中,随时检查合同,查看填写错误提示,确认合同填写无误后,再将合同发给进口商。进口商收到合同之后,进行检查,如果没有问题,盖章然后确认合同。
根据使用的贸易术语以及运输方式不同,各个交易所需要填写的单据以及内容都不相同。进口商需要填写“境外汇款申请书”,支付定金,还需要办理外汇检测系统网上申报等。作为出口商,我需要收取银行发来的进口商已付款的入账通知,进行备货,填写商业发票,装箱单,国际空运委托书(或者国际海运委托书。根据贸易术语是否需要给货物投保,如果需要,出口商还要填写货物运输保险单。之后还需要出口报关,发送通知装运等一系列操作。进口商在收到单据后需要提货,进口报关,销货等操作,这样一笔真正的交易才算完成。
二、 实训总结
在做单的过程中,我发现函电部分相对比较简单,报价核算以及各种单据的填写是一个难点。在之前的学习中,我们也上过单证课和国贸课程。之前学习的都是一些理论知识,这次才真正地加以实践。
首先,在做国际业务的过程中,少不了与外国进出口商的函电往来。贸易函电是国际贸易实务中最为重要的一环。在写函电的过程中,要选择正确的“业务种类”,明示标题。同时,要注意选择合适的贸易函电的格式,比如齐头式、缩进式等。除此之外,语言的流畅简洁,语法正确也是很重要的。在发盘的函电当中,必要的磋商内容要素要涉及到,比如:品名,规格或质量要求,数量,包装,价格,运输方式,装运时间,装运港,目的港,保险条款等。
其次,在填写单证的过程中,虽然之前学过单证课,但是刚开始做的时候还是一头雾水,需要按照百科示例一个条目一个条目地填写,后来,业务做的多了,单据也填写的更加顺畅了,当然,一些不太确定的还需要查询一下百科当中的示例。
本次实训,对不同的贸易术语,都有涉及。例如:FOB,CIF,FCA,CPT等,也使用了空运海运等不同的运输方式。在使用中发现,这些贸易术语实践起来比理论学习要复杂的多。刚开始做单的时候,出错比较多,得分也比较低,但是随着做单越来越多,计算准确率也升高了,分数也慢慢高了上去。经过了一个月的练习与实践,对这些贸易术语以及术语涉及到的计算有了更深的理解和认识。
三、 实训心得
刚开始接触pocib的时候,对于软件的不熟悉,使我一头雾水,遇到问题不知道如何解决。后来,慢慢地调整好心态,虽然做前两笔的时候很生疏,但是后来慢慢的对软件越来越熟悉,对单证填写和计算越来越得心应手,做单的速度也越来越快。
在实训做单的过程中,收获很大。随着经济全球化和国际贸易的发展,外贸人才的需求量也越来越大。通过pocib实训,可以使我们熟悉现实的国际贸易环境,帮助我们在短期内全面体验国际贸易企业运作的过程,加强了我们对国际贸易知识的感性认识,提高了我们的业务能力。在实训过程中,货物有可能也会遇险,需要进行一系列的索赔,这对提高我们处理风险的能力有很大的帮助。
在实训过程中,也发现了自己的一些不足。在函电往来时,拘泥于一些函电模板,对于自己真实地写作水平并没有得到很好的检验。在实训的过程中,有些单据填写错误和核算错误是由于自己的粗心导致的,在以后的工作中,一定要认真仔细,否则可能会对企业的利润造成损失。
总的来说,为期一个月的实训使我收获颇丰。对外贸业务有了系统而完整的认识,并且通过实际操作,接触到了真实地外贸环境,对日后从事外贸工作有很大帮助。
问题出现原因:重复依赖了库,去掉一个就行了
C++对象成员与对象成员指针的创建与使用 一、概念: 1.对象成员的概念:
C++对象成员就是类定义中类的成员对象,通俗来讲就是A类的对象作为B类的成员出现。 2.对象成员指针的概念:
C++对象成员就是类定义中类的成员对象指针,通俗来讲就是A类的对象指针作为B类的成员出现。 二、实现方式: 1.对象成员定义方式:
//基础类(坐标类) class Coordinate { private: int m_ix; int m_iy; public: Coordinate(int x,int y); ~Coordinate(); int getx(); int gety(); }; //引用类(线段类) class Line { private: //----对象成员定义方式--------- Coordinate pcoorA; //Coordinate的对象成员作为其数据成员 Coordinate pcoorB; public: Line(int x1,int y1,int x2,int y2); ~Line(); void printInfo(); }; 2.对象成员指针定义方式:
//基础类(坐标类) class Coordinate { private: int m_ix; int m_iy; public: Coordinate(int x,int y); ~Coordinate(); int getx(); int gety(); }; //引用类(线段类) class Line { private: //----对象成员指针定义方式------ Coordinate *m_pcoorA; //Coordinate的对象成员指针作为其数据成员 Coordinate *m_pcoorB; public: Line(int x1,int y1,int x2,int y2); ~Line(); void printInfo(); }; 三、实例化: !!!实例化顺序:实例化基础类(Coordinate类)--->实例化引用类(Line类)
文章目录 一、安装mysql二、创建实例并启动三、测试连接 一、安装mysql 可先配置docker镜像加速:参考
运行命令下载安装镜像
sudo docker pull mysql:5.7 如下为安装完成
二、创建实例并启动 创建容器并运行
sudo docker run -p 3306:3306 --name mysql \ -v/mydata/mysql/log:/var/log/mysql \ -v/mydata/mysql/data:/var/lib/mysql \ -v/mydata/mysql/conf:/etc/mysql \ -e MYSQL_ROOT_PASSWORD=root \ -d mysql:5.7 更新容器,设置自启动
docker update --restart=always mysql 参数说明
docker run -p 3306:3306 --name mysql \ 将主机的3306端口映射到Docker的3306端口,起一个mysql别名 -v/mydata/mysql/log:/var/log/mysql \ 将日志文件夹挂载到主机 -v/mydata/mysql/data:/var/lib/mysql \ 将配置文件夹挂载到主机 -v/mydata/mysql/conf:/etc/mysql \ 将配置文件夹挂载到主机 -e MYSQL_ROOT_PASSWORD=root \ 初始化 root用户的密码 -d mysql:5.7 选择的镜像 查看运行中的容器
sudo docker ps 三、测试连接 默认账号密码为 root root(如果连接不了,建议更换centos7版本)
转载自https://blog.csdn.net/rocling/article/details/90290576 https://zhuanlan.zhihu.com/p/28023308 把答案放在前面 L0范数是指向量中非0的元素的个数。(L0范数很难优化求解)
L1范数是指向量中各个元素绝对值之和
L2范数是指向量各元素的平方和然后求平方根
L1范数可以进行特征选择,即让特征的系数变为0.
L2范数可以防止过拟合,提升模型的泛化能力,有助于处理 condition number不好下的矩阵(数据变化很小矩阵求解后结果变化很大)
(核心:L2对大数,对outlier离群点更敏感!)
下降速度:最小化权值参数L1比L2变化的快
模型空间的限制:L1会产生稀疏 L2不会。
L1会趋向于产生少量的特征,而其他的特征都是0,而L2会选择更多的特征,这些特征都会接近于0。
概念 范数是具有“长度”概念的函数。在向量空间内,为所有的向量的赋予非零的增长度或者大小。不同的范数,所求的向量的长度或者大小是不同的。
举个例子,2维空间中,向量(3,4)的长度是5,那么5就是这个向量的一个范数的值,更确切的说,是欧式范数或者L2范数的值。
特别的,L0范数:指向量中非零元素的个数。无穷范数:指向量中所有元素的最大绝对值。
1.监督学习的基本模型 监督机器学习问题无非就是“minimize your error while regularizing your parameters”,也就是在规则化参数的同时最小化误差。最小化误差是为了让我们的模型拟合我们的训练数据,而规则化参数是防止我们的模型过分拟合我们的训练数据。因为参数太多,会导致我们的模型复杂度上升,容易过拟合,也就是我们的训练误差会很小。但训练误差小并不是我们的最终目标,我们的目标是希望模型的测试误差小,也就是能准确的预测新的样本。所以,我们需要保证模型“简单”的基础上最小化训练误差,这样得到的参数才具有好的泛化性能(也就是测试误差也小),而模型“简单”就是通过规则函数来实现的。另外,规则项的使用还可以约束我们的模型的特性。这样就可以将人对这个模型的先验知识融入到模型的学习当中,强行地让学习到的模型具有人想要的特性,例如稀疏、低秩、平滑等等。要知道,有时候人的先验是非常重要的。前人的经验会让你少走很多弯路,这就是为什么我们平时学习最好找个大牛带带的原因。一句点拨可以为我们拨开眼前乌云,还我们一片晴空万里,醍醐灌顶。对机器学习也是一样,如果被我们人稍微点拨一下,它肯定能更快的学习相应的任务。只是由于人和机器的交流目前还没有那么直接的方法,目前这个媒介只能由规则项来担当了。
还有几种角度来看待规则化的。规则化符合奥卡姆剃刀(Occam’s razor)原理。它的思想很平易近人:在所有可能选择的模型中,我们应该选择能够很好地解释已知数据并且十分简单的模型。从贝叶斯估计的角度来看,规则化项对应于模型的先验概率。民间还有个说法就是,规则化是结构风险最小化策略的实现,是在经验风险上加一个正则化项(regularizer)或惩罚项(penalty term)。
一般来说,监督学习可以看做最小化下面的目标函数:
其中,第一项L(yi,f(xi;w)) 衡量我们的模型(分类或者回归)对第i个样本的预测值f(xi;w)和真实的标签yi之前的误差。因为我们的模型是要拟合我们的训练样本的嘛,所以我们要求这一项最小,也就是要求我们的模型尽量的拟合我们的训练数据。但正如上面说言,我们不仅要保证训练误差最小,我们更希望我们的模型测试误差小,所以我们需要加上第二项,也就是对参数w的规则化函数Ω(w)去约束我们的模型尽量的简单。
OK,到这里,如果你在机器学习浴血奋战多年,你会发现,哎哟哟,机器学习的大部分带参模型都和这个不但形似,而且神似。是的,其实大部分无非就是变换这两项而已。对于第一项Loss函数,如果是Square loss,那就是最小二乘了;如果是Hinge Loss,那就是著名的SVM了;如果是exp-Loss,那就是牛逼的 Boosting了;如果是log-Loss,那就是Logistic Regression了;还有等等。不同的loss函数,具有不同的拟合特性,这个也得就具体问题具体分析的。但这里,我们先不究loss函数的问题,我们把目光转向“规则项Ω(w)”。
规则化函数Ω(w)也有很多种选择,一般是模型复杂度的单调递增函数,模型越复杂,规则化值就越大。比如,规则化项可以是模型参数向量的范数。然而,不同的选择对参数w的约束不同,取得的效果也不同,但我们在论文中常见的都聚集在:零范数、一范数、二范数、迹范数、Frobenius范数和核范数等等。这么多范数,到底它们表达啥意思?具有啥能力?什么时候才能用?什么时候需要用呢?不急不急,下面我们挑几个常见的娓娓道来。
2.L0范数与L1范数 L0范数是指向量中非0的元素的个数。如果我们用L0范数来规则化一个参数矩阵W的话,就是希望W的大部分元素都是0。这太直观了,太露骨了吧,换句话说,让参数W是稀疏的。OK,看到了“稀疏”二字,大家都应该从当下风风火火的“压缩感知”和“稀疏编码”中醒悟过来,原来用的漫山遍野的“稀疏”就是通过这玩意来实现的。但你又开始怀疑了,是这样吗?看到的papers世界中,稀疏不是都通过L1范数来实现吗?脑海里是不是到处都是||W||1影子呀!几乎是抬头不见低头见。没错,这就是这节的题目把L0和L1放在一起的原因,因为他们有着某种不寻常的关系。那我们再来看看L1范数是什么?它为什么可以实现稀疏?为什么大家都用L1范数去实现稀疏,而不是L0范数呢?
L1范数是指向量中各个元素绝对值之和,也有个美称叫“稀疏规则算子”(Lasso regularization)。现在我们来分析下这个价值一个亿的问题:为什么L1范数会使权值稀疏?有人可能会这样给你回答“它是L0范数的最优凸近似”。实际上,还存在一个更美的回答:任何的规则化算子,如果他在Wi=0的地方不可微,并且可以分解为一个“求和”的形式,那么这个规则化算子就可以实现稀疏。这说是这么说,W的L1范数是绝对值,|w|在w=0处是不可微,但这还是不够直观。这里因为我们需要和L2范数进行对比分析。所以关于L1范数的直观理解,请待会看看第二节。
对了,上面还有一个问题:既然L0可以实现稀疏,为什么不用L0,而要用L1呢?个人理解一是因为L0范数很难优化求解(NP难问题),二是L1范数是L0范数的最优凸近似,而且它比L0范数要容易优化求解。所以大家才把目光和万千宠爱转于L1范数。
OK,来个一句话总结:L1范数和L0范数可以实现稀疏,L1因具有比L0更好的优化求解特性而被广泛应用。
好,到这里,我们大概知道了L1可以实现稀疏,但我们会想呀,为什么要稀疏?让我们的参数稀疏有什么好处呢?这里扯两点:
1)特征选择(Feature Selection):
大家对稀疏规则化趋之若鹜的一个关键原因在于它能实现特征的自动选择。一般来说,xi的大部分元素(也就是特征)都是和最终的输出yi没有关系或者不提供任何信息的,在最小化目标函数的时候考虑xi这些额外的特征,虽然可以获得更小的训练误差,但在预测新的样本时,这些没用的信息反而会被考虑,从而干扰了对正确yi的预测。稀疏规则化算子的引入就是为了完成特征自动选择的光荣使命,它会学习地去掉这些没有信息的特征,也就是把这些特征对应的权重置为0。
2)可解释性(Interpretability):
另一个青睐于稀疏的理由是,模型更容易解释。例如患某种病的概率是y,然后我们收集到的数据x是1000维的,也就是我们需要寻找这1000种因素到底是怎么影响患上这种病的概率的。假设我们这个是个回归模型:y=w1x1+w2x2+…+w1000x1000+b(当然了,为了让y限定在[0,1]的范围,一般还得加个Logistic函数)。通过学习,如果最后学习到的w就只有很少的非零元素,例如只有5个非零的wi,那么我们就有理由相信,这些对应的特征在患病分析上面提供的信息是巨大的,决策性的。也就是说,患不患这种病只和这5个因素有关,那医生就好分析多了。但如果1000个wi都非0,医生面对这1000种因素,累觉不爱。
3.L2范数 除了L1范数,还有一种更受宠幸的规则化范数是L2范数: ||W||2。它也不逊于L1范数,它有两个美称,在回归里面,有人把有它的回归叫“岭回归”(Ridge Regression),有人也叫它“权值衰减weight decay”。这用的很多吧,因为它的强大功效是改善机器学习里面一个非常重要的问题:过拟合。至于过拟合是什么,上面也解释了,就是模型训练时候的误差很小,但在测试的时候误差很大,也就是我们的模型复杂到可以拟合到我们的所有训练样本了,但在实际预测新的样本的时候,糟糕的一塌糊涂。通俗的讲就是应试能力很强,实际应用能力很差。擅长背诵知识,却不懂得灵活利用知识。例如下图所示(来自Ng的course):
上面的图是线性回归,下面的图是Logistic回归,也可以说是分类的情况。从左到右分别是欠拟合(underfitting,也称High-bias)、合适的拟合和过拟合(overfitting,也称High variance)三种情况。可以看到,如果模型复杂(可以拟合任意的复杂函数),它可以让我们的模型拟合所有的数据点,也就是基本上没有误差。对于回归来说,就是我们的函数曲线通过了所有的数据点,如上图右。对分类来说,就是我们的函数曲线要把所有的数据点都分类正确,如下图右。这两种情况很明显过拟合了。
OK,那现在到我们非常关键的问题了,为什么L2范数可以防止过拟合?回答这个问题之前,我们得先看看L2范数是个什么东西。
L2范数是指向量各元素的平方和然后求平方根。我们让L2范数的规则项||W||2最小,可以使得W的每个元素都很小,都接近于0,但与L1范数不同,它不会让它等于0,而是接近于0,这里是有很大的区别的哦。而越小的参数说明模型越简单,越简单的模型则越不容易产生过拟合现象。为什么越小的参数说明模型越简单?我也不懂,我的理解是:限制了参数很小,实际上就限制了多项式某些分量的影响很小(看上面线性回归的模型的那个拟合的图),这样就相当于减少参数个数。其实我也不太懂,希望大家可以指点下。
这里也一句话总结下:通过L2范数,我们可以实现了对模型空间的限制,从而在一定程度上避免了过拟合。限制解空间范围,缩小解空间,来控制模型复杂度,降低结构化风险。
L2范数的好处是什么呢?这里也扯上两点:
1)学习理论的角度:
从学习理论的角度来说,L2范数可以防止过拟合,提升模型的泛化能力。
2)优化计算的角度:
从优化或者数值计算的角度来说,L2范数有助于处理 condition number不好的情况下矩阵求逆很困难的问题。(这个不明白)
直观地聊聊L1和L2的差别 为什么一个让绝对值最小,一个让平方最小,会有那么大的差别呢?我看到的有两种几何上直观的解析:
递归算法时间复杂度的计算方程式一个递归方程:
在引入递归树之前可以考虑一个例子:
T(n) = 2T(n/2) + n2
迭代2次可以得:
T(n) = n2 + 2(2T(n/4) + (n/2) 2)
还可以继续迭代,将其完全展开可得:
T(n) = n2 + 2((n/2) 2 + 2((n/22)2 + 2((n/23) 2 + 2((n/24) 2 +…+2((n/2i) 2 + 2T(n/2i + 1)))…)))) ……(1)
而当n/2i+1 == 1时,迭代结束。
将(1)式小括号展开,可得:
T(n) = n2 + 2(n/2)2 + 22(n/22) 2 + … + 2i(n/2i)2 + 2i+1T(n/2i+1)
这恰好是一个树形结构,由此可引出递归树法。
图中的(a)(b)(c)(d)分别是递归树生成的第1,2,3,n步。每一节点中都将当前的自由项n2留在其中,而将两个递归项T(n/2) + T(n/2)分别摊给了他的两个子节点,如此循环。
图中所有节点之和为:
[1 + 1/2 + (1/2)2 + (1/2)3 + … + (1/2)i] n2 = 2n2
田海立@csdn 2020-10-3
AI Benchmark这里特指ETHZ(苏黎世联邦理工学院)的AI性能评测工具。最新其发布了v4版本以及基于这个版本的soc和手机AI性能数据。本文分析了AI Benchmark测试的原理,v4版本的变化,以及榜单头部海思麒麟990 5G与MTK天玑1000+的对比。据此也就能解读AI-Benchmark榜单上各个数据的含义了。
我尽量用公开的技术和客观的数据来说话,不用非公开渠道获得的信息,仅代表个人观点,不代表所服务公司态度。但有涉密或不可公开信息,会及时删除。
一、AI Benchmark测试相关 1. 移动端侧AI推理的碎片化 AI的发展,先是有了各家设备的私有实现,然后才有AndroidNN试图统一Android平台上端侧的推理框架。所以AI性能评测工具也就有了安兔兔、鲁大师的基于soc厂商私有sdk的实现,也就有了ETHZ基于AndroidNN的实现。安兔兔、鲁大师等起个大早赶了个晚集,AI评测领域并没有多大声音;而各soc厂商(高通、华为、MTK、紫光展锐...)却不断拿AI Benchmark说事,发布新芯片时宣传其AI性能。
2. AI Benchmark测试原理 AI Benchmark是基于AndroidNN的,所以讲AI Benchmark的原理离不开AndroidNN。先看一下,AndroidNN的框架:
上层通过NNAPI提供API给上层使用,注意这里的API还是比较lower level的,对于不是关注AI实现的开发者来说不是很友好的。Android NN在诞生之初就不是提供给最终应用开发者的,更上层是推荐TensorFlow Lite这样的基于Model级别的ML框架的。
NN HAL定义一层NN device的抽象层,屏蔽细节,只要DSP/GPU/NPU基于NN HAL实现,NN Runtime就能基于其能力(Capabilities)来调度使用。
所以,AI Benchmark在这个生态链里,也就是应用层的概念,只是TFLite因为晚于AndroidNN集成于Android,所以TFLite是直接被打包于AI Benchmark的apk里的。
AI性能除了运行时间之外,基于数据集的精度也是一个很重要的指标。试想如果一个NN Device跑的是很快,但是丢失关键的数据结果都不对,那快又如何。所以,AI Benchmark里集成了很多的TFLite模型来分别测试CPU/INT8/FP16/FP32/INIT数据;还内置数据集测试特定测试项来评估精度数据。
二、AI Benchmark v4更新 前面说了v3之前的测试项,当然这也是一个变化的过程,比如性能测试是v3才加入的。
今年5月31号,AI Benchmark官方网站上公布了v4版本的变化,这里简要总结一下:
1. TFLite delegates 因为最近TFLite的变化,对Device利用Delegate机制直接做了支持,这些包括github里GPU的直接支持,也与Qualcomm合作实现了Hexagon的支持(与Samsung的合作未在TensorFlow官方发布,但AI Benchmark那里应该拿到了Samsung提供的Delegate支持)。
所以,v4版本里Qualcomm与Samsung的TFLite Delegate支持直接集成到了apk里,这两家的手机直接通过TFLite Delegate支持。
2. NNAPI-1.2 v3版本的时候,NNAPI-1.2还没正式发布,所以v3版本之前不会有NN1.2的特性。在V4版本里,针对NNAPI设计了case。
3. 并行执行 NNAPI可以看到可以有多个NN Device,这些Device是可以并行执行的。当然这里的并行也可以是不同数据类型的模型的支持。这里也有了很好的设计与实现。
4. 评分系统 这里数据集与测试模型以及测试项都有了变化,所以评分系统也有相应的变化。
不过这里,并没有详细列举测试项以及对应比值,而v3版本在其paper里是有详细描述评分标准里各个测试项所占的比重的。这里可以期待其在最新paper里公开,也或许可能不对大众开放了,仅对其合作伙伴开放。
三、AI Benchmark v4测试数据简要解读 AI Benchmark网站上已经发布了v4的性能数据,移动端数据包括了phone数据与soc数据。
Phone数据里采用同一soc的厂家差别都不大,所以手机厂商所做的努力有限,基本还是由soc来决定。但phone厂家的这点微小的差异也反应了各家的实力。
这里来看soc榜单
1. 执行设备 MTK/海思使用的NN device,也就是TFLite -> NNAPI -> NNHAL到NN Device这一同之前版本一样的路径;