数据专栏

智能大数据搬运工,你想要的我们都有

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

「深度学习福利」大神带你进阶工程师,立即查看>>>
关于人脸识别 目前的人脸识别已经相对成熟,有各种收费免费的商业方案和开源方案,其中OpenCV很早就支持了人脸识别,在我选择人脸识别开发库时,也横向对比了三种库,包括在线识别的百度、开源的OpenCV和商业库虹软(中小型规模免费)。
百度的人脸识别,才上线不久,文档不太完善,之前联系百度,官方也给了我基于Android的Example,但是不太符合我的需求,一是照片需要上传至百度服务器(这个是最大的问题),其次,人脸的定位需要自行去实现(捕获到人脸后上传进行识别)。
OpenCV很早以前就用过,当时做人脸+车牌识别时,最先考虑的就是OpenCV,但是识别率在当时不算很高,后来是采用了一个电子科大的老师自行开发的识别库(相对易用,识别率也还不错),所以这次准备做时,没有选择OpenCV。
虹软其实在无意间发现的,当时正在寻找开发库,正在测试Python的一个方案,就发现有新闻说虹软的识别库全面开放并且可以免费使用,而且是离线识别,所以就下载尝试了一下,发现识别率还不错,所以就暂定了采用虹软的识别方案。这里主要就给大家分享一下开发过程当中的一些坑和使用心得,顺便开源识别库的C# Wrapper。
SDK的C# Wrapper 由于虹软的库是采用C++开发的,而我的应用程序采用的是C#,所以,需要对库进行包装,便于C#的调用,包装的主要需求是可以在C#中快速方便的调用,无需考虑内存、指针等问题,并且具备一定的容错性。Wrapper库目前已经开源,大家可以到Github上进行下载,地址点击这里。Wrapper库基本上没有什么可以说的,无非是对PInvoke的包装,只是里面做了比较多的细节处理,屏蔽了调用细节,提供了相对高层的函数。有兴趣的可以看看源代码。
Wrapper库的使用例子 基本使用
注意使用之前,在虹软申请了新的Key后,需要同时更新libs下的三个dll文件,key和sdk的版本是相关联的,否则会抛出异常。
人脸检测(静态图片): using (var detection = LocatorFactory.GetDetectionLocator("appId", "sdkKey")) { var image = Image.FromFile("test.jpg"); var bitmap = new Bitmap(image); var result = detection.Detect(bitmap, out var locateResult); //检测到位置信息在使用完毕后,需要释放资源,避免内存泄露 using (locateResult) { if (result == ErrorCode.Ok && locateResult.FaceCount > 0) { using (var g = Graphics.FromImage(bitmap)) { var face = locateResult.Faces[0].ToRectangle(); g.DrawRectangle(new Pen(Color.Chartreuse), face.X, face.Y, face.Width, face.Height); } bitmap.Save("output.jpg", ImageFormat.Jpeg); } } }
人脸跟踪(人脸跟踪一般用于视频的连续帧识别,相较于检测,又更高的执行效率,这里用静态图片做例子,实际使用和检测没啥区别): using (var detection = LocatorFactory.GetTrackingLocator("appId", "sdkKey")) { var image = Image.FromFile("test.jpg"); var bitmap = new Bitmap(image); var result = detection.Detect(bitmap, out var locateResult); using (locateResult) { if (result == ErrorCode.Ok && locateResult.FaceCount > 0) { using (var g = Graphics.FromImage(bitmap)) { var face = locateResult.Faces[0].ToRectangle(); g.DrawRectangle(new Pen(Color.Chartreuse), face.X, face.Y, face.Width, face.Height); } bitmap.Save("output.jpg", ImageFormat.Jpeg); } } }
人脸对比: using (var proccesor = new FaceProcessor("appid", "locatorKey", "recognizeKey", true)) { var image1 = Image.FromFile("test2.jpg"); var image2 = Image.FromFile("test.jpg"); var result1 = proccesor.LocateExtract(new Bitmap(image1)); var result2 = proccesor.LocateExtract(new Bitmap(image2)); //FaceProcessor是个整合包装类,集成了检测和识别,如果要单独使用识别,可以使用FaceRecognize类 //这里做演示,假设图片都只有一张脸 //可以将FeatureData持久化保存,这个即是人脸特征数据,用于后续的人脸匹配 //File.WriteAllBytes("XXX.data", feature.FeatureData);FeatureData会自动转型为byte数组 if ((result1 != null) & (result2 != null)) Console.WriteLine(proccesor.Match(result1[0].FeatureData, result2[0].FeatureData, true)); }
使用注意事项
LocateResult(检测结果)和Feature(人脸特征)都包含需要释放的内存资源,在使用完毕后,记得需要释放,否则会引起内存泄露。FaceProcessor和FaceRecognize的Match函数,在完成比较后,可以自动释放,只需要最后两个参数指定为true即可,如果是用于人脸匹配(1:N),则可以采用默认参数,这种情况下,第一个参数指定的特征数据不会自动释放,用于循环和特征库的特征进行比对。
整合的完整例子 在Github上,有完整的FaceDemo例子,里面主要实现了通过ffmpeg采集RTSP协议的图像(使用海康的摄像机),然后进行人脸匹配。在开发过程中遇到不少的坑。
人脸识别的首要工作就是捕获摄像机视频帧,这一块上是坑的最久的,因为最开始采用的是OpenCV的包装库,Emgu.CV,在开发过程中,捕获USB摄像头时,倒是问题不大,没有出现过异常。在捕获RTSP视频流时,会不定时的出现AccessviolationException异常,短则几十分钟,长则几个小时,总之就是不稳定。在官方Github地址上,也提了Issue,他们给出的答复是屏蔽的我业务逻辑,仅捕获视频流试试,结果问题依然,所以,我基本坑定了试Emgu.CV上面的问题。后来经过反复的实验,最终确定了选择ffmpeg。
ffmepg主要采用ProcessStartInfo进行调用,我采用的是NReco.VideoConverter(一个ffmpeg调用的包装,可以通过nuget搜索安装),虽然ffmpeg解决了稳定性问题,但是实际开发时,也遇到了不少坑,其中,最主要的是NReco.VideoConverter没有任何文档和例子(实际有,需要75刀购买),所以,自己研究了半天,如何捕获视频流并转换为Bitmap对象。只要实现这一步,后续就是调用Wrapper就行了。
FaceDemo详解
上面说到了,通过ffmpeg捕获视频流并转换Bitmap是重点,所以,这里也主要介绍这一块。
首先是ffmpeg的调用参数: var setting = new ConvertSettings { CustomOutputArgs = "-an -r 15 -pix_fmt bgr24 -updatefirst 1" }; //-s 1920x1080 -q:v 2 -b:v 64k task = ffmpeg.ConvertLiveMedia("rtsp://admin:12qwaszxA@192.168.1.64:554/h264/ch1/main/av_stream", null, outputStream, Format.raw_video, setting); task.OutputDataReceived += DataReceived; task.Start();
-an表示不捕获音频流,-r表示帧率,根据需求和实际设备调整此参数,-pix_fmt比较重要,一般情况下,指定为bgr24不会有太大问题(还是看具体设备),之前就是用成了rgb24,结果捕获出来的图像,人都变成阿凡达了,颜色是反的。最后一个参数,坑的我差点放弃这个方案。本身,ffmpeg在调用时,需要指定一个文件名模板,捕获到的输出会按照模板生成文件,如果要将数据输出到控制台,则最后传入一个-即可,最开始没有指定updatefirst,ffmpeg在捕获了第一帧后就抛出了异常,最后查了半天ffmpeg说明(完整参数说明非常多,输出到文本有1319KB),发现了这个参数,表示持续更新第一个文件。最后,在调用视频捕获是,需要指定输出格式,必须指定为Format.raw_video,实际上这个格式名称有些误导人,按道理将应该叫做raw_image,因为最终输出的是每帧原始的位图数据。
到此为止,还并没有解决视频流数据的捕获,因为又来一个坑,ProcessStartInfo的控制台缓冲区大小只有32768 bytes,即,每一次的输出,实际上并不是一个完整的位图数据。
//完整代码参加Github源代码 //代码片段1 private Bitmap _image; private IntPtr _pImage; { _pImage = Marshal.AllocHGlobal(1920 * 1080 * 3); _image = new Bitmap(1920, 1080, 1920 * 3, PixelFormat.Format24bppRgb, _pImage); } //代码片段2 private MemoryStream outputStream; private void DataReceived(object sender, EventArgs e) { if (outputStream.Position == 6220800) lock (_imageLock) { var data = outputStream.ToArray(); Marshal.Copy(data, 0, _pImage, data.Length); outputStream.Seek(0, SeekOrigin.Begin); } }
花了不少时间摸索(不要看只有几行,人都整崩溃了),得出了上述代码。首先,我捕获的图像数据是24位的,并且图像大小是1080p的,所以,实际上,一个原始位图数据的大小为stride * height,即width * 3 * height,大小为6220800 bytes。所以,在判断了捕获数据到达这个大小后,就进行Bitmap转换处理,然后将MemoryStream的位置移动到最开始。需要注意的时,由于捕获到的是原始数据(不包含bmp的HeaderInfo),所以注意看Bitmap的构造方式,是通过一个指向原始数据位置的指针就行构造的,更新该图像时,也仅需要更新指针指向的位置数据即可,无需在建立新的Bitmap实例。
位图数据获取到了,就可以进行识别处理了,高高兴兴的加上了识别逻辑,但是现实总是充满了意外和惊喜,没错,坑又来了。没有加入识别逻辑的时候,捕获到的图像在PictureBox上显示非常正常,清晰、流畅,加上识别逻辑后,开始出现花屏(捕获到的图像花屏)、拖影、显示延迟(至少会延迟10-20秒以上)、程序卡顿,总之就是各种问题。最开始,我的识别逻辑写到DataReceived方法里面的,这个方法是运行于主线程外的另一个线程中的,其实按道理将,捕获、识别、显示位于一个线程中,应该是不会出现问题,我估计(不确定,没有去深入研究,如果谁知道实际原因,可以留言告诉我),是因为ffmpeg的原因,因为ffmpeg是单独的一个进程在跑,他的数据捕获是持续在进行的,而识别模块的处理时间大于每一帧的采集时间,所以,缓冲区中的数据没有得到及时处理,ffmpeg接收到的部分图像数据(大于32768的数据)被丢弃了,然后就出现了各种问题。最后,又是一次耗时不短的探索之旅。 private void Render() { while (_renderRunning) { if (_image == null) continue; Bitmap image; lock (_imageLock) { image = (Bitmap) _image.Clone(); } if (_shouldShot){ WriteFeature(image); _shouldShot = false; } Verify(image); if (videoImage.InvokeRequired) videoImage.Invoke(new Action(() => { videoImage.Image = image; })); else videoImage.Image = image; } }
如上代码所述,我单独开了一个线程,用于图像的识别处理和显示,每次都从已捕获到的图像中克隆出新的Bitmap实例进行处理。这种方式的缺点在于,有可能会导致丢帧的现象,因为上面说到了,识别时间(如果检测到新的人脸,那么加上匹配,大约需要130ms左右)大于每帧时间,但是并不影响识别效果和需求的实现,基本丢弃的帧可以忽律。最后,运行,稳定了、完美了,实际也感觉不到丢帧。
Demo程序,我运行了大约4天左右,中间没有出现过任何异常和识别错误。
写在最后 虽然虹软官方表示,免费识别库适用于1000人脸库以下的识别,实际上,做一定的工作(工作量其实也不小),也是可以实现较大规模的人脸搜索滴。例如,采用多线程进行匹配,如果人脸库人脸数量大于1000,则可以考虑每个线程分别进行处理,人脸特征数据做缓存(一个人脸的特征数据是22KB,对内存要求较高),以提升程序的识别搜索效率。或者人脸库特别大的情况下,可以采用分布式处理,人脸特征加载到Redis数据库当中,多个进程多个线程读取处理,每个线程上传自己的识别结果,然后主进程做结果合并判断工作,主要的挑战就在于多线程的工作分配一致性和对单点故障的容错性。
更新:
DEMO中的例子采用了IP Camera,一般情况下,大家可能用USB Camera居多,所以,更新了源代码,增加了USB Camera的例子,只需要屏蔽掉IP Camara代码即可。
task = ffmpeg.ConvertLiveMedia("video=USB2.0 PC CAMERA", "dshow", outputStream, Format.raw_video, setting); 需要注意的有以下几点:
设备名称可以通过控制面板或者ffmpeg的命令获取:ffmpeg -list_devices true -f dshow -i dummy 注意修改捕获的图像大小,一般USB摄像头是640*480,更新的代码增加了全局变量,可以直接修改。 如果要查询USB摄像头支持的分辨率,也可以通过ffmpeg命令:ffmpeg -list_options true -f dshow -i video="USB2.0 PC CAMERA" 更新2:
源代码中新增了对 .net core 2.0的支持,因为用到了GDI+相关函数,所以用的是CoreCompat/System.Drawing,所以在部署环境下需要安装libgdiplus, apt-get intall libgdiplus。
另外,有关于视频流的采集,除了使用FFMEPG和一些开源的开发库外,也可以使用厂商的SDK,不过之前试过海康的SDK,那叫一个难用啊,所以大家自己选择吧。
更新3:
虹软SDK更新了新的功能,开发包同步更新,支持年龄和性别的评估。
人工智能
2019-04-09 14:53:00
「深度学习福利」大神带你进阶工程师,立即查看>>> AFR_FSDKInterface engine = new AFR_FSDKEngine(); //用来存放提取到的人脸信息, face_1 是注册的人脸,face_2 是要识别的人脸 AFR_FSDKFace face1 = new AFR_FSDKFace(); AFR_FSDKFace face2 = new AFR_FSDKFace(); //初始化人脸识别引擎,使用时请替换申请的 APPID 和 SDKKEY AFR_FSDKError error = engine.AFR_FSDK_InitialEngine("APPID", "SDKKEY"); Log.d("com.arcsoft", "AFR_FSDK_InitialEngine = " + error.getCode()); //输入的 data 数据为 NV21 格式(如 Camera 里 NV21 格式的 preview 数据);人脸坐标一般使用人脸检测返回的 Rect 传入;人脸角度请按照人脸检测引擎返回的值传入。 error = engine.AFR_FSDK_ExtractFRFeature(data1, width, height, AFR_FSDKEngine.CP_PAF_NV21, new Rect(210, 178, 478, 446), AFR_FSDKEngine.AFR_FOC_0, face1); Log.d("com.arcsoft", "Face=" + face1.getFeatureData()[0]+ "," + face1.getFeatureData()[1] + "," + face1.getFeatureData()[2] + "," + error.getCode()); error = engine.AFR_FSDK_ExtractFRFeature(data1, width, height, AFR_FSDKEngine.CP_PAF_NV21, new Rect(210, 170, 470, 440), AFR_FSDKEngine.AFR_FOC_0, face2); Log.d("com.arcsoft", "Face=" + face2.getFeatureData()[0]+ "," + face2.getFeatureData()[1] + "," + face2.getFeatureData()[2] + "," + error.getCode()); //score 用于存放人脸对比的相似度值 AFR_FSDKMatching score = new AFR_FSDKMatching(); error = engine.AFR_FSDK_FacePairMatching(face1, face2, score); Log.d("com.arcsoft", "AFR_FSDK_FacePairMatching=" + error.getCode()); Log.d("com.arcsoft", "Score:" + score.getScore()); //销毁人脸识别引擎 error = engine.AFR_FSDK_UninitialEngine(); Log.d("com.arcsoft", "AFR_FSDK_UninitialEngine : " + error.getCode());
人工智能
2019-04-08 17:08:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
前言
人工智能时代快速来临,其中人脸识别是当前比较热门的技术,在国内也越来越多的运用,例如刷脸打卡、刷脸App,身份识别,人脸门禁等等。当前的人脸识别技术分为WEBAPI和SDK调用两种法方式,WEBAPI需要实时联网,SDK调用可以离线使用。
Android作为一个比较广泛的平台,如何实现人脸识别功能呢?
Android人脸识别
Android实现人脸识别可以通过google原生自带或第三方提供,googel自带的只能识别静态图片,第三方提供的功能比较强大。
google官方自带
google通过FaceDetector类实现人脸识别功能,查看官方说明:Identifies the faces of people in a Bitmap graphic object.
识别结果
第三方SDK提供
Opencv
opencv官方网站 https://opencv.org/,Github地址是https://github.com/opencv/opencv,作为开发人员第一步是有一个可以运行的项目,里面有sample例子,依次打开opencv- >sample->android,选择项目导入运行。
虹软免费SDK
官方地址 http://www.arcsoft.com.cn/index.html,跟一般SDK类似,需要注册才能使用,网上有很多教程,接入简单。 参考:Android人脸识别开发入门--基于虹软免费SDK实现
Face++
官方地址: https://www.faceplusplus.com.cn/,好像是要收费的。接入请参考:ANDROID使用FACE++架构包实现人脸识别
科大讯飞人脸识别
官方地址: http://www.xfyun.cn/services/face?type=face,科大讯飞的语音云技术一直是遥遥领先,人脸识别官方并没有提供具体的参考示例,可能还没开放。
人工智能
2019-04-08 16:28:00
「深度学习福利」大神带你进阶工程师,立即查看>>>

上周就关于《结构化感知机标注框架的内容》已经分享了一篇《分词工具Hanlp基于感知机的中文分词框架》,本篇接上一篇内容,继续分享词性标注与命名实体识别框架的内容。
词性标注
训练
词性标注是分词后紧接着的一个任务,训练语料同上,接口如下:

命令行
java -cp hanlp.jar com.hankcs.hanlp.model.perceptron.Main -task POS -train -reference data/test/pku98/199801.txt -model data/test/perceptron/pos.bin

API
public void testTrain() throws Exception
{
PerceptronTrainer trainer = new POSTrainer();
trainer.train("data/test/pku98/199801.txt", Config.POS_MODEL_FILE);
}

测试
词性标注器接受的输入不再是纯文本,而是分词后的单词数组或列表:

public void testLoad() throws Exception
{
PerceptronPOSTagger tagger = new PerceptronPOSTagger(Config.POS_MODEL_FILE);
System.out.println(Arrays.toString(tagger.tag("中国 交响乐团 谭利华 在 布达拉宫 广场 演出".split(" "))));
}
正常情况下输出每个单词的词性:
[ns, n, nr, p, ns, n, v]
关于如何组合分词器和词性标注器,使其同时进行分词与词性标注,请参考接下来的章节。

命名实体识别
目前本系统默认支持人名(nr),地名(ns),机构名(nt)三种命名实体的识别,用户可以重载NERTrainer的createTagSet来支持任意NER类型。
训练
命名实体识别是词性标注的后续任务,训练语料依然同上,接口如下:

命令行
java -cp hanlp.jar com.hankcs.hanlp.model.perceptron.Main -task NER -train -reference data/test/pku98/199801.txt -model data/test/perceptron/ner.bin

API
public void testTrain() throws Exception
{
PerceptronTrainer trainer = new NERTrainer();
trainer.train("data/test/pku98/199801.txt", Config.NER_MODEL_FILE);
}

自定义NER类型
重载NERTrainer的createTagSet来支持自己的NER类型。当然,用户提供的语料必须满足2014人民日报格式。

PerceptronTrainer trainer = new NERTrainer()
{
@Override
protected TagSet createTagSet()
{
NERTagSet tagSet = new NERTagSet();
tagSet.nerLabels.add("YourNER1");
tagSet.nerLabels.add("YourNER2");
tagSet.nerLabels.add("YourNER3");
return tagSet;
}
};
测试
命名实体识别器的输入不再是纯文本,而是分词结果与词性标注结果:

public void testTag() throws Exception
{
PerceptionNERecognizer recognizer = new PerceptionNERecognizer(Config.NER_MODEL_FILE);
System.out.println(Arrays.toString(recognizer.recognize("吴忠市 乳制品 公司 谭利华 来到 布达拉宫 广场".split(" "), "ns n n nr p ns n".split(" "))));
}

正常情况下输出:
[B-nt, M-nt, E-nt, S, O, S, O]
7个标签代表上述7个词语所属的命名实体成分。
人工智能
2019-04-08 13:51:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
自从2012年AlexNet网络在ImageNet挑战赛上取得巨大成功之后,计算机视觉和深度学习等领域再一次迎来研究热潮。计算机视觉,从字面意义上理解就是让计算机等机器也具备人类视觉,研究让机器进行图像分类、目标检测等。在这近十年里,该领域取得的成就让人大吃一惊,有些研究已经超越了人类的表现水平。
对于想入门或者从事深度学习领域的工作者而言,一般是从计算机视觉入手,网上有很多资料去介绍理论方面的知识,进行相应的实践也必不可少。本文总结一些在计算机视觉和深度学习领域的一些实践项目,供读者针对自己的感兴趣点挑选并实践。
如果不熟悉上述术语,可以从下面的文章中了解更多的相关信息: 一份“新奇”的深度学习介绍 两个月探索深度学习和计算机视觉 从神经科学到计算机视觉 Andrew Ng在Coursera上的计算机视觉课程
下面进行正文介绍:
1.使用OpenCV进行手部动作跟踪
项目地址: akshaybahadur21/HandMovementTracking 为了执行视频跟踪,算法分析连续视频帧并输出帧之间的目标移动。针对这类问题,有各种各样的算法,每种算法都有各自的优缺点。在选择使用哪种算法时,针对实际应用场景考虑是很重要的。视觉跟踪系统有两个主要组成部分:目标表示和局部化,以及过滤和数据关联。
视频跟踪是使用相机随时间定位移动物体(或多个物体)的过程。它有多种用途,比如:人机交互、安全和监控、视频通信和压缩、增强现实、交通控制、医学成像和视频编辑等。
项目代码如下: import numpy as np import cv2 import argparse from collections import deque cap=cv2.VideoCapture(0) pts = deque(maxlen=64) Lower_green = np.array([110,50,50]) Upper_green = np.array([130,255,255]) while True: ret, img=cap.read() hsv=cv2.cvtColor(img,cv2.COLOR_BGR2HSV) kernel=np.ones((5,5),np.uint8) mask=cv2.inRange(hsv,Lower_green,Upper_green) mask = cv2.erode(mask, kernel, iterations=2) mask=cv2.morphologyEx(mask,cv2.MORPH_OPEN,kernel) #mask=cv2.morphologyEx(mask,cv2.MORPH_CLOSE,kernel) mask = cv2.dilate(mask, kernel, iterations=1) res=cv2.bitwise_and(img,img,mask=mask) cnts,heir=cv2.findContours(mask.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2:] center = None if len(cnts) > 0: c = max(cnts, key=cv2.contourArea) ((x, y), radius) = cv2.minEnclosingCircle(c) M = cv2.moments(c) center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"])) if radius > 5: cv2.circle(img, (int(x), int(y)), int(radius),(0, 255, 255), 2) cv2.circle(img, center, 5, (0, 0, 255), -1) pts.appendleft(center) for i in xrange (1,len(pts)): if pts[i-1]is None or pts[i] is None: continue thick = int(np.sqrt(len(pts) / float(i + 1)) * 2.5) cv2.line(img, pts[i-1],pts[i],(0,0,225),thick) cv2.imshow("Frame", img) cv2.imshow("mask",mask) cv2.imshow("res",res) k=cv2.waitKey(30) & 0xFF if k==32: break # cleanup the camera and close any open windows cap.release() cv2.destroyAllWindows()
54行代码即可解决这个问题,很简单吧!该项目依赖Opencv包,需要在计算机中安装OpenCV才能执行这个项目,下面是针对Mac系统、Ubuntu系统以及Windows系统的安装方法: Mac系统: 如何在MacOS上安装OpenCV3 Ubuntu系统: OpenCV:在Ubuntu中安装OpenCV-Python Windows系统: 在Windows中安装OpenCV-Python
2.使用OpenCV进行睡意检测
项目地址: akshaybahadur21/Drowsiness_Detection
睡意检测对于长时间驾驶、工厂生产等场景很重要,由于长时间开车时,容易产生睡意,进而可能导致事故。当用户昏昏欲睡时,此代码可以通过检测眼睛来发出警报。
依赖包 CV2 immutils DLIB SciPy
实现算法
每只眼睛用6个(x,y)坐标表示,从眼睛的左角开始,然后顺时针绕眼睛工作:
条件
它检查20个连续帧,如果眼睛纵横比低于0.25,则生成警报。
关系
实现
https://cdn-images-1.medium.com/max/800/1*rA3cxL2429vOi4jsuscPug.gif
3.使用Softmax回归进行数字手写体识别
项目地址: akshaybahadur21/Digit-Recognizer
此代码使用softmax回归对不同的数字进行分类,依赖的包有点多,可以安装下Conda,它包含了机器学习所需要的所有依赖包。
描述
Softmax回归(同义词:多项Logistic、最大熵分类器、多类Logistic回归)是逻辑回归的推广,可以将其用于多类分类(假设类间是互斥的)。相反,在二分类任务中使用(标准)Logistic回归模型。
Python实现
使用的是MNIST数字手写体数据集,每张图像大小为28×28,这里尝试使用三种方法对0到9的数字进行分类,分别是Logistic回归、浅层神经网络和深度神经网络。 Logistic回归: import numpy as np import matplotlib.pyplot as plt def softmax(z): z -= np.max(z) sm = (np.exp(z).T / np.sum(np.exp(z), axis=1)) return sm def initialize(dim1, dim2): """ :param dim: size of vector w initilazied with zeros :return: """ w = np.zeros(shape=(dim1, dim2)) b = np.zeros(shape=(10, 1)) return w, b def propagate(w, b, X, Y): """ :param w: weights for w :param b: bias :param X: size of data(no of features, no of examples) :param Y: true label :return: """ m = X.shape[1] # getting no of rows # Forward Prop A = softmax((np.dot(w.T, X) + b).T) cost = (-1 / m) * np.sum(Y * np.log(A)) # backwar prop dw = (1 / m) * np.dot(X, (A - Y).T) db = (1 / m) * np.sum(A - Y) cost = np.squeeze(cost) grads = {"dw": dw, "db": db} return grads, cost def optimize(w, b, X, Y, num_iters, alpha, print_cost=False): """ :param w: weights for w :param b: bias :param X: size of data(no of features, no of examples) :param Y: true label :param num_iters: number of iterations for gradient :param alpha: :return: """ costs = [] for i in range(num_iters): grads, cost = propagate(w, b, X, Y) dw = grads["dw"] db = grads["db"] w = w - alpha * dw b = b - alpha * db # Record the costs if i % 50 == 0: costs.append(cost) # Print the cost every 100 training examples if print_cost and i % 50 == 0: print("Cost after iteration %i: %f" % (i, cost)) params = {"w": w, "b": b} grads = {"dw": dw, "db": db} return params, grads, costs def predict(w, b, X): """ :param w: :param b: :param X: :return: """ # m = X.shape[1] # y_pred = np.zeros(shape=(1, m)) # w = w.reshape(X.shape[0], 1) y_pred = np.argmax(softmax((np.dot(w.T, X) + b).T), axis=0) return y_pred def model(X_train, Y_train, Y,X_test,Y_test, num_iters, alpha, print_cost): """ :param X_train: :param Y_train: :param X_test: :param Y_test: :param num_iterations: :param learning_rate: :param print_cost: :return: """ w, b = initialize(X_train.shape[0], Y_train.shape[0]) parameters, grads, costs = optimize(w, b, X_train, Y_train, num_iters, alpha, print_cost) w = parameters["w"] b = parameters["b"] y_prediction_train = predict(w, b, X_train) y_prediction_test = predict(w, b, X_test) print("Train accuracy: {} %", sum(y_prediction_train == Y) / (float(len(Y))) * 100) print("Test accuracy: {} %", sum(y_prediction_test == Y_test) / (float(len(Y_test))) * 100) d = {"costs": costs, "Y_prediction_test": y_prediction_test, "Y_prediction_train": y_prediction_train, "w": w, "b": b, "learning_rate": alpha, "num_iterations": num_iters} # Plot learning curve (with costs) #costs = np.squeeze(d['costs']) #plt.plot(costs) #plt.ylabel('cost') #plt.xlabel('iterations (per hundreds)') #plt.title("Learning rate =" + str(d["learning_rate"])) #plt.plot() #plt.show() #plt.close() #pri(X_test.T, y_prediction_test) return d def pri(X_test, y_prediction_test): example = X_test[2, :] print("Prediction for the example is ", y_prediction_test[2]) plt.imshow(np.reshape(example, [28, 28])) plt.plot() plt.show() 浅层神经网络: import numpy as np import matplotlib.pyplot as plt def softmax(z): z -= np.max(z) sm = (np.exp(z).T / np.sum(np.exp(z),axis=1)) return sm def layers(X, Y): """ :param X: :param Y: :return: """ n_x = X.shape[0] n_y = Y.shape[0] return n_x, n_y def initialize_nn(n_x, n_h, n_y): """ :param n_x: :param n_h: :param n_y: :return: """ np.random.seed(2) W1 = np.random.randn(n_h, n_x) * 0.01 b1 = np.random.rand(n_h, 1) W2 = np.random.rand(n_y, n_h) b2 = np.random.rand(n_y, 1) parameters = {"W1": W1, "b1": b1, "W2": W2, "b2": b2} return parameters def forward_prop(X, parameters): W1 = parameters['W1'] b1 = parameters['b1'] W2 = parameters['W2'] b2 = parameters['b2'] Z1 = np.dot(W1, X) + b1 A1 = np.tanh(Z1) Z2 = np.dot(W2, A1) + b2 A2 = softmax(Z2.T) cache = {"Z1": Z1, "A1": A1, "Z2": Z2, "A2": A2} return A2, cache def compute_cost(A2, Y, parameters): m = Y.shape[1] W1 = parameters['W1'] W2 = parameters['W2'] logprobs = np.multiply(np.log(A2), Y) cost = - np.sum(logprobs) / m cost = np.squeeze(cost) return cost def back_prop(parameters, cache, X, Y): m = Y.shape[1] W1 = parameters['W1'] W2 = parameters['W2'] A1 = cache['A1'] A2 = cache['A2'] dZ2 = A2 - Y dW2 = (1 / m) * np.dot(dZ2, A1.T) db2 = (1 / m) * np.sum(dZ2, axis=1, keepdims=True) dZ1 = np.multiply(np.dot(W2.T, dZ2), 1 - np.square(A1)) dW1 = (1 / m) * np.dot(dZ1, X.T) db1 = (1 / m) * np.sum(dZ1, axis=1, keepdims=True) grads = {"dW1": dW1, "db1": db1, "dW2": dW2, "db2": db2} return grads def update_params(parameters, grads, alpha): W1 = parameters['W1'] b1 = parameters['b1'] W2 = parameters['W2'] b2 = parameters['b2'] dW1 = grads['dW1'] db1 = grads['db1'] dW2 = grads['dW2'] db2 = grads['db2'] W1 = W1 - alpha * dW1 b1 = b1 - alpha * db1 W2 = W2 - alpha * dW2 b2 = b2 - alpha * db2 parameters = {"W1": W1, "b1": b1, "W2": W2, "b2": b2} return parameters def model_nn(X, Y,Y_real,test_x,test_y, n_h, num_iters, alpha, print_cost): np.random.seed(3) n_x,n_y = layers(X, Y) parameters = initialize_nn(n_x, n_h, n_y) W1 = parameters['W1'] b1 = parameters['b1'] W2 = parameters['W2'] b2 = parameters['b2'] costs = [] for i in range(0, num_iters): A2, cache = forward_prop(X, parameters) cost = compute_cost(A2, Y, parameters) grads = back_prop(parameters, cache, X, Y) if (i > 1500): alpha1 = 0.95*alpha parameters = update_params(parameters, grads, alpha1) else: parameters = update_params(parameters, grads, alpha) if i % 100 == 0: costs.append(cost) if print_cost and i % 100 == 0: print("Cost after iteration for %i: %f" % (i, cost)) predictions = predict_nn(parameters, X) print("Train accuracy: {} %", sum(predictions == Y_real) / (float(len(Y_real))) * 100) predictions=predict_nn(parameters,test_x) print("Train accuracy: {} %", sum(predictions == test_y) / (float(len(test_y))) * 100) #plt.plot(costs) #plt.ylabel('cost') #plt.xlabel('iterations (per hundreds)') #plt.title("Learning rate =" + str(alpha)) #plt.show() return parameters def predict_nn(parameters, X): A2, cache = forward_prop(X, parameters) predictions = np.argmax(A2, axis=0) return predictions 深度神经网络: import numpy as np import matplotlib.pyplot as plt def softmax(z): cache = z z -= np.max(z) sm = (np.exp(z).T / np.sum(np.exp(z), axis=1)) return sm, cache def relu(z): """ :param z: :return: """ s = np.maximum(0, z) cache = z return s, cache def softmax_backward(dA, cache): """ :param dA: :param activation_cache: :return: """ z = cache z -= np.max(z) s = (np.exp(z).T / np.sum(np.exp(z), axis=1)) dZ = dA * s * (1 - s) return dZ def relu_backward(dA, cache): """ :param dA: :param activation_cache: :return: """ Z = cache dZ = np.array(dA, copy=True) # just converting dz to a correct object. dZ[Z <= 0] = 0 return dZ def initialize_parameters_deep(dims): """ :param dims: :return: """ np.random.seed(3) params = {} L = len(dims) for l in range(1, L): params['W' + str(l)] = np.random.randn(dims[l], dims[l - 1]) * 0.01 params['b' + str(l)] = np.zeros((dims[l], 1)) return params def linear_forward(A, W, b): """ :param A: :param W: :param b: :return: """ Z = np.dot(W, A) + b cache = (A, W, b) return Z, cache def linear_activation_forward(A_prev, W, b, activation): """ :param A_prev: :param W: :param b: :param activation: :return: """ if activation == "softmax": Z, linear_cache = linear_forward(A_prev, W, b) A, activation_cache = softmax(Z.T) elif activation == "relu": Z, linear_cache = linear_forward(A_prev, W, b) A, activation_cache = relu(Z) cache = (linear_cache, activation_cache) return A, cache def L_model_forward(X, params): """ :param X: :param params: :return: """ caches = [] A = X L = len(params) // 2 # number of layers in the neural network # Implement [LINEAR -> RELU]*(L-1). Add "cache" to the "caches" list. for l in range(1, L): A_prev = A A, cache = linear_activation_forward(A_prev, params["W" + str(l)], params["b" + str(l)], activation='relu') caches.append(cache) A_last, cache = linear_activation_forward(A, params["W" + str(L)], params["b" + str(L)], activation='softmax') caches.append(cache) return A_last, caches def compute_cost(A_last, Y): """ :param A_last: :param Y: :return: """ m = Y.shape[1] cost = (-1 / m) * np.sum(Y * np.log(A_last)) cost = np.squeeze(cost) # To make sure your cost's shape is what we expect (e.g. this turns [[17]] into 17). return cost def linear_backward(dZ, cache): """ :param dZ: :param cache: :return: """ A_prev, W, b = cache m = A_prev.shape[1] dW = (1. / m) * np.dot(dZ, cache[0].T) db = (1. / m) * np.sum(dZ, axis=1, keepdims=True) dA_prev = np.dot(cache[1].T, dZ) return dA_prev, dW, db def linear_activation_backward(dA, cache, activation): """ :param dA: :param cache: :param activation: :return: """ linear_cache, activation_cache = cache if activation == "relu": dZ = relu_backward(dA, activation_cache) dA_prev, dW, db = linear_backward(dZ, linear_cache) elif activation == "softmax": dZ = softmax_backward(dA, activation_cache) dA_prev, dW, db = linear_backward(dZ, linear_cache) return dA_prev, dW, db def L_model_backward(A_last, Y, caches): """ :param A_last: :param Y: :param caches: :return: """ grads = {} L = len(caches) # the number of layers m = A_last.shape[1] Y = Y.reshape(A_last.shape) # after this line, Y is the same shape as A_last dA_last = - (np.divide(Y, A_last) - np.divide(1 - Y, 1 - A_last)) current_cache = caches[-1] grads["dA" + str(L)], grads["dW" + str(L)], grads["db" + str(L)] = linear_activation_backward(dA_last, current_cache, activation="softmax") for l in reversed(range(L - 1)): current_cache = caches[l] dA_prev_temp, dW_temp, db_temp = linear_activation_backward(grads["dA" + str(l + 2)], current_cache, activation="relu") grads["dA" + str(l + 1)] = dA_prev_temp grads["dW" + str(l + 1)] = dW_temp grads["db" + str(l + 1)] = db_temp return grads def update_params(params, grads, alpha): """ :param params: :param grads: :param alpha: :return: """ L = len(params) // 2 # number of layers in the neural network for l in range(L): params["W" + str(l + 1)] = params["W" + str(l + 1)] - alpha * grads["dW" + str(l + 1)] params["b" + str(l + 1)] = params["b" + str(l + 1)] - alpha * grads["db" + str(l + 1)] return params def model_DL( X, Y, Y_real, test_x, test_y, layers_dims, alpha, num_iterations, print_cost): # lr was 0.009 """ Implements a L-layer neural network: [LINEAR->RELU]*(L-1)->LINEAR->SIGMOID. Arguments: X -- data, numpy array of shape (number of examples, num_px * num_px * 3) Y -- true "label" vector (containing 0 if cat, 1 if non-cat), of shape (1, number of examples) layers_dims -- list containing the input size and each layer size, of length (number of layers + 1). alpha -- learning rate of the gradient descent update rule num_iterations -- number of iterations of the optimization loop print_cost -- if True, it prints the cost every 100 steps Returns: params -- params learnt by the model. They can then be used to predict. """ np.random.seed(1) costs = [] # keep track of cost params = initialize_parameters_deep(layers_dims) for i in range(0, num_iterations): A_last, caches = L_model_forward(X, params) cost = compute_cost(A_last, Y) grads = L_model_backward(A_last, Y, caches) if (i > 800 and i<1700): alpha1 = 0.80 * alpha params = update_params(params, grads, alpha1) elif(i>=1700): alpha1 = 0.50 * alpha params = update_params(params, grads, alpha1) else: params = update_params(params, grads, alpha) if print_cost and i % 100 == 0: print("Cost after iteration %i: %f" % (i, cost)) if print_cost and i % 100 == 0: costs.append(cost) predictions = predict(params, X) print("Train accuracy: {} %", sum(predictions == Y_real) / (float(len(Y_real))) * 100) predictions = predict(params, test_x) print("Test accuracy: {} %", sum(predictions == test_y) / (float(len(test_y))) * 100) #plt.plot(np.squeeze(costs)) #plt.ylabel('cost') #plt.xlabel('iterations (per tens)') #plt.title("Learning rate =" + str(alpha)) #plt.show() return params def predict(parameters, X): A_last, cache = L_model_forward(X, parameters) predictions = np.argmax(A_last, axis=0) return predictions
通过网络摄像头执行写入
运行代码 python Dig-Rec.py
https://cdn-images-1.medium.com/max/800/1*WTV3DSu0HUnoN2HI6c5wbw.gif
通过网络摄像头显示图像
运行代码 python Digit-Recognizer.py
https://cdn-images-1.medium.com/max/800/1*f_XcMY2RSBGDlhElBwRqnA.gif
梵文字母识别
项目地址: akshaybahadur21/Devanagiri-Recognizer
此代码可帮助您使用CNN对不同梵文字母进行分类。
使用技术
使用了卷积神经网络,使用Tensorflow作为框架和Keras API来提供高级抽象。
网络结构
CONV2D→MAXPOOL→CONV2D→MAXPOOL→FC→Softmax→Classification
额外的要点
1.可以尝试不同的网络结构;
2.添加正则化以防止过拟合;
3.可以向训练集添加其他图像以提高准确性;
Python实现
使用Dataset-DHCD(Devnagari Character Dataset)数据集,每张图大小为32 X 32,详细代码请参考项目地址。
运行代码 python Dev-Rec.py
https://cdn-images-1.medium.com/max/800/1*VKAL2X-Up49EklMvm-h6qg.gif
4.使用FaceNet进行面部识别
项目地址: akshaybahadur21/Face-Recoinion
此代码使用 facenet 进行面部识别。facenet的概念最初是在一篇研究论文中提出的,主要思想是谈到三重损失函数来比较不同人的图像。为了提供稳定性和更好的检测,额外添加了自己的几个功能。
本项目依赖的包如下 : numpy matplotlib cv2 keras dlib h5py scipy
描述
面部识别系统是能够从数字图像或来自视频源的视频帧识别或验证人的技术。面部识别系统有多种方法,但一般来说,它们都是通过将给定图像中的选定面部特征与数据库中的面部进行比较来工作。
功能增加
1.仅在眼睛睁开时检测脸部(安全措施);
2.使用dlib中的面部对齐功能在实时流式传输时有效预测;
Python实现
1.网络使用 - Inception Network;
2.原始论文 - Google的Facenet;
程序
1.如果想训练网络,运行 Train-inception.py ,但是不需要这样做,因为已经训练了模型并将其保存为 face-rec_Google.h5 ,在运行时只需加载这个文件即可;
2.现在需要在数据库中放置一些图像。检查 /images 文件夹,可以将图片粘贴到此,也可以使用网络摄像头拍摄;
3.运行 rec-feat.py 以运行该应用程序;
https://cdn-images-1.medium.com/max/800/1*sNLiysO0eCj49kg3ZR3m_Q.gif
5.表情识别器
项目地址 akshaybahadur21/Emojinator
此项目可识别和分类不同的表情符号。但是目前为止,只支持手势表达的情绪。
代码依赖包 numpy matplotlib cv2 keras dlib h5py scipy
描述
表情符号是电子信息和网页中使用的表意文字和表情的符号。表情符号存在于各种类型中,包括面部表情、常见物体、地点和天气以及动物等。
功能
1.用于检测手的过滤器;
2.CNN用于训练模型;
Python实现
1.网络使用 - 卷积神经网络
程序
1.首先,创建一个手势数据库。为此,运行 CreateGest.py 。尝试在框架内稍微移动一下手,以确保模型在训练时不会发生过拟合;
2.对所需的所有功能重复此操作;
3.运行 CreateCSV.py 将图像转换为CSV文件;
4.训练模型,运行 TrainEmojinator.py ;
5.运行 Emojinator.py 通过网络摄像头测试模型;
https://cdn-images-1.medium.com/max/800/1*S__r_PVF7Q1Egvl3iWts8g.gif
总结
这些项目都令人印象深刻,所有这些项目都可以在计算机上运行。如果你不想安装任何东西,可以更轻松地在Deep Cognition平台上运行,并且可以在线运行。
感谢网络上各种开源项目的贡献。尝试一下各种感兴趣的项目,运行它们并获得灵感。上述这些这只是深度学习和计算机视觉可以做的事情中的一小部分,还有很多事情可以尝试,并且可以将其转变为帮助世界变得更美好的事情,code change world!
此外,每个人都应该对许多不同的事情感兴趣。我认为这样可以改善世界、改善生活、改善工作方式,思考和解决问题的方式,如果我们依据现在所掌握的资源,使这些知识领域共同发挥作用,我们就可以在世界和生活中产生巨大的积极影响。
作者:【方向】
原文链接 ​
本文为云栖社区原创内容,未经允许不得转载。
人工智能
2019-04-08 13:16:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
机器学习有望从根本上改变软件开发的本质,这也许是自FORTRAN和LISP被发明以来软件开发领域改变最大的一次。这些变化对数百万正在从事软件开发的人而言,意味着什么呢?失业?裁员?现有的软件开发将变得面目全非?
自20世纪70年代以来,我们尽可能的构建了足够多的软件。我们有高级语言,低级语言,脚本语言以及用于构建和测试软件的工具,但我们利用这些工具做的事情却没有发生太大变化。我们现在拥有的语言和工具比50年前要好得多,但它们本质上是一样的。我们仍然使用代码编辑器,但这些编辑器变得更花哨了:他们有彩色的高亮,变量名补全,它们有时可以帮助我们完成重构等任务,但他们仍然是emacs和vi的后代。面向对象编程代表了一种不同的编程风格,但从某种本质上而言并不是“全新”的事物,对于函数式编程我们可以一直追溯到50年代。
未来我们将专注于机器学习而不是人工智能。机器学习曾经被称为“AI最管用的那一部分”,更重要的是,“机器学习”这种提法可以避开类似“通用智能”这种叫法。因为这样的系统目前不存在,并且可能永远不存在,目前来看只有人类才能做到这一点。而机器学习可能只比模式识别多一点点,但我们已经看到模式识别可以完成很多工作。实际上,手工编码的模式识别是我们当前工具集的核心:这真的是现代优化编译器所正在做的。
麦肯锡估计“使用现有技术,只有不到5%的职业可以完全自动化。然而,大约有60%的职业工作活动中,具有30%或更多的组成部分能够被自动化。”软件开发和数据科学不会成为完全被自动化的职业之一。但优秀的软件开发人员一直在寻求对于繁琐,重复的任务的自动化,毫无疑问,软件开发本身将日益变得可以被自动化。这并不是一个激进的愿景,因为我们在过去的半个世纪里持续的为了自动化工具而努力。编译器对编写机器代码的过程进行了自动化。脚本语言通过将更大,更复杂的程序粘合在一起来自动执行许多枯燥无味的任务。软件测试工具、自动部署工具、容器和容器编排系统等等,这些都是为了对开发、部署、管理软件系统的过程进行自动化的工具。而且这些都没有利用机器学习,但这肯定是下一步它们要做的。 机器学习会不会吞并软件?
毕竟,“软件吞噬世界”是一个日益抽象和普遍化的过程。笔记本电脑,手机或智能手表已经逐渐取代收音机,电视机,锁和钥匙,电灯开关,这是因为我们并没有将计算机仅仅看成数字计算器而是通用机器。从这个角度来看,很容易将机器学习想象成下一个抽象层次,这是我们迄今为止发现的最通用的问题解决工具。当然,神经网络已经证明了它们可以执行许多特定任务。由资深人士乐观地表示,对于许多任务而言,收集数据比编写程序更容易。对于一些非常有趣且困难的程序,这无疑是正确的,比如说收集围棋或国际象棋的训练数据很容易,但很难写一个程序成功地玩这些游戏。另一方面,数据收集并不总是那么容易。我们无法设想自动标记图片的程序,特别是在Facebook和阿里巴巴这样收集了数十亿张图片的网站,而且其中许多图片已被人类标记过。对于像人脸识别这样的任务,我们不知道如何编写软件,而且很难收集数据。对于其他任务,例如计费,可以很容易地根据一些简单的业务规则编写程序。如果你能够收集数据,你编写的程序将更好地适应不同的情况,还能够检测异常,这一点当“将人类纳入软件迭代的循环”时,尤为如此。 正在代替代码的机器学习
机器学习正在使代码变得高效:Google的Jeff Dean说,500行TensorFlow代码已经取代了谷歌翻译中的500000行代码。虽然代码行数是一个值得质疑的指标,但无论是从编程工作量角度来看还是从需要维护的代码量来看,这个突破都是可称赞的。更重要的是这段代码是如何工作的:相比于五十万行的代码,这是一个经过训练以用于翻译的神经网络。神经网络可以随着语言的变化和使用场景的变化,在新数据上被重新训练,而且整个代码都不需要重写。虽然我们不应低估训练任何复杂度的神经网络的难度,但我们同样也不应低估管理和调试一个巨大代码库带来的问题。
研究表明,神经网络可以通过组合现有模块来创建新程序。虽然以这种方式构建的程序很简单,但是让单个神经网络能够学习执行几个不同的任务是很重要的,每个任务通常都需要一个单独的程序。
Pete Warden认为:“开发人员必须成为一名教师(教机器),一名训练数据的策划人。”我们发现,这种说法非常具有启发性。软件开发不会消失,但开发人员必须以不同的方式来思考自己。你如何构建一个解决一般问题的系统,然后教该系统解决一个特定的任务?这貌似听起来像是一个风险很高又麻烦的场景。但这意味着我们的系统将变得更加灵活,具有很强的适应性。Warden设想的未来,更多是关于产出的,而不是关于撰写代码行数。Peter经过更加系统的思考,认为机器学习可以从训练数据中产生短程序,而不是很大的程序。 数据管理和基础设施
早期的迹象表明,机器学习有着可以胜过传统的数据库索引的性能:它可以学习预测数据的存储位置或者预测数据是否存在。机器学习明显更快,并且需要更少的内存,但也有着相当大的限制性:当前基于机器学习的工具不包括多维索引,并假设数据库不经常更新。重新训练比重建传统数据库索引需要更长的时间。尽管如此,研究人员正在研究如何学习到多维索引,查询的优化,重新训练的性能。
机器学习已经进入了数据基础设施的其他领域。数据工程师正在使用机器学习来管理Hadoop,从而可以更快地响应Hadoop集群中的内存不足等问题。 Kafka工程师使用机器学习来诊断问题,从而简化了管理许多配置的问题,这些配置会影响数据库的性能。数据工程师和数据库管理员不会过时,但他们可能需要发展一下他们的机器学习技能。机器学习将帮助他们使困难的问题变得更简单,管理数据基础架构这个工作,将不会像正确设置数百个不同的配置参数那样,它会更像是在训练一个系统,让整个管理工作运行的更有条理。
使困难问题变得可管理是数据科学最重要的问题之一。数据工程师负责维护数据管道:提取数据、清理数据、特征工程和模型构建。同时他们还需要负责在非常复杂的环境中部署软件,一旦部署了这些基础架构,还需要不断监视它,以检测(或防止)资源用尽,确保模型正确运行。这些都是非常适合用机器学习处理的任务,我们越来越多地看到像MLFlow这样的软件能够被用于管理数据管道。 数据科学
在自动化编程的早期表现形式中,工具旨在使数据分析师能够执行更高级的分析任务。Automatic Statistician是一种更新的工具,可自动进行探索性数据分析,并为时间序列类型的数据提供统计模型,且附有详细说明。
随着深度学习的兴起,数据科学家发现自己需要寻找合适的神经网络架构和参数。让神经网络学着找到合适自己架构的过程,也可能被自动化。毕竟,神经网络就只是单纯的自动化学习工具:虽然构建神经网络结构需要大量的人力工作,但是不可能手动调整模型的所有参数。未来的场景应该是使用机器学习来探索所有可能的神经网络架构:正如一篇文章指出的,10层网络可能就有10的10次方种可能性。已经有其他研究人员使用强化学习来让神经网络架构开发变得更加容易。
模型创建不是一劳永逸的事情:数据模型需要不断进行测试和调整。我们开始看到的用于持续监控和模型调整的这些工具并不是特别新颖,比如用于A/B测试的老虎机算法已经存在了一段时间,对于许多公司来说,老虎机算法算是强化学习的第一步。机器学习同样也可以用来查找软件中的漏洞,有些系统会浏览代码,并寻找已知的缺陷。这些系统不一定需要能够修复代码,也不承诺找到所有潜在的问题。但是他们可以很容易地对危险的代码进行高亮显示,并且他们可以允许在大型代码库上进行开发的程序员提出诸如“还有类似这样的问题存在于其他地方吗?”之类的问题。
游戏开发者也正在探索利用机器学习来降低游戏开发成本以及创造更多有趣的游戏。机器学习可以用来制作看起来更逼真的背景和场景吗?游戏开发者都知道对逼真的场景和图像进行绘制和建模又耗钱,又费时。目前,非玩家角色(NPC)所做的一切都必须明确编程。机器学习可以用来模拟NPC的行为吗?如果NPC可以学习到行为,我们可以期待更有创意的游戏玩法出现。 展望未来
软件开发人员的未来是什么样的?软件开发是否会同样走上麦肯锡为其他行业预测的演化路径呢?在软件开发和数据科学中所涉及的30%的工作是否会被自动化?也许,刚刚我们所谈的只是对未来某种情况的简单解读。但毫无疑问,机器学习将改变软件开发。如果未来我们现在所认为的“编程”中很大一部分被自动化了,那也没什么好惊讶的。编译器不进行机器学习,但他们通过自动生成机器代码来改变软件行业,这在未来可能并不是什么新鲜事。
重要的问题是软件开发和数据科学将如何变化。一种可能性,实际上是一种事实:软件开发人员会在数据收集和准备方面投入更多精力。没有数据训练,机器学习就什么都不是。开发人员必须做的,不仅仅是收集数据; 他们必须构建数据管道,以及构建管理这些管道的基础设施,我们称之为“数据工程”。在许多情况下,这些管线本身将使用机器学习来监控和优化自己。
我们可以看到训练机器学习算法成为一个独特的子专业;我们可能很快会有一个新职业-“训练工程师”,就像我们目前谈论的“数据工程师”一样。Andrew Ng在他自己的《机器学习渴望》一书时中说:“这本书的重点不是教你ML算法,而是教你如何让ML算法有效。没有编码,也没有复杂的数学。本书几乎完全侧重于模型训练过程,而不仅仅是编码,训练才是让机器学习有效工作的本质。”
我们提出的想法都涉及一种能力:它使人类能够生产出更快、更可靠、更好的能够生效的产品。开发人员将能够将更多时间花在有趣且更重要的问题上,而不是把基本工作做好。那些问题可能是什么问题呢?
在一篇关于智能增强的讨论里,Nicky论证了,计算机在针对一个问题寻找最佳答案上表现出色。因为计算机的本质是计算工具。但是他们不是很擅长“找到一个值得回答的有趣问题”,这件事是人类做的。那么,我们需要提出哪些重要的问题呢?
这些重要的问题已经在不断的被发现了,比如我们刚刚开始认识到道德在计算中的重要性,才开始考虑更好的用户界面,包括会话界面:它们将如何运作?即使在人工智能的帮助下,我们的安全问题也不会消失。先不管安全问题怎样,我们所有的设备都在变得“聪明”。这意味着什么?我们希望它们做什么?人类不会编写尽可能多的低级代码。但是正因为他们将不会去编写那些代码,所以他们可以自由地思考代码应该做什么,以及它应该如何与人交互。需要解决的问题永远不会少。
很难想象“人类不再创建软件”的未来,但很容易想象“将人纳入软件研发的循环”中在未来将占越来越多的比重。
本文由阿里云云栖社区组织翻译。
文章原标题《what-machine-learning-means-for-software-development》作者:Ben Lorica,O'Reilly Media的首席数据科学家
作者:【方向】
原文链接
本文为云栖社区原创内容,未经允许不得转载。
人工智能
2019-04-08 11:20:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
在 lucene排序算法之向量空间模型(一) 中说明了采用余弦距离而不是欧式距离来度量查询向量与文档向量相似度的原因,本文主要推导lucene中文档相似度公式:
1、令查询向量 ,文档向量 ;
2、 ,
3、由于用户在输入查询串时,一般不会输入相同的查询词,因此简单设置 ;
;需要注意的是只有当词都存在于q和d中wn*Wn才不为0。
4、查询向量长度 ,可能大家会认为由于评分时需要比较的是查询向量与文档向量的相似度,查询向量的长度对于所有文档的相似度而言是相同的,因此实际中就可以舍去,但实际并没有舍去;
5、对于文档向量长度,lucene并没有采用标准的公式即:|d|=开根号(W1*W1+...+Wn*Wn),而是设置成默认的开根号(词的个数)
6、文档得分score= ,实际上当|d|不是标准公式的时候,最终的得分也就不是两个向量的余弦了,因此该公式要换个角度去理解,score= ,变成标准的单位查询向量q和非标准的文档向量d的乘积, 也就是文档向量d在单位查询向量q的投影长度,这样说明lucene实际上并没有把余弦距离当作相似度量,单位查询向量实际上指明了查询的方向,哪个文档向量在查询向量上的投影长度越长说明就越相似。余弦距离实际是两个单位向量间的乘积,lucene的官方文档中也说明了这一点,我们也可以看成是其中一个单位向量在另一个单位向量上的投影长度, 不知lucene官方是否这样定义得分公式的,但个人认为能够解释得通 。
注意:这里不是lucene的最终公式,因为还没有考虑各项自定义的权重问题,以及文档向量|d|的实际取值问题,最终公式下一篇给出。
人工智能
2019-04-05 20:59:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
手上有一个项目,需要检验使用本程序的,是否本人!因为在程序使用前,我们都已经做过头像现场采集,所以源头呢是不成问题的,那么人脸检测,人脸比对,怎么办呢?度娘了下,目前流行的几个人脸检测,人脸比对核心,大多都是基于互联网的,但我们的项目是基于本地服务器,那就有点麻烦了,后来找到ArcFace.它的核心允许本地调用,那就好办了,立刻去了他们的网站下载sdk,看论坛,下DEMO;我当时下的是这个:ArcFace C#DEMO
本以为可以一帆风顺的就可以把项目搞定了,不想…噩梦才刚刚开始呢…且听我细细道来:
首先说下我的调用逻辑;
项目里有一个采集端(每个业务窗口),负责采集现场人像,并通过ArcFace人脸检测,特征提取,获取到.dat比对源(ServiceFaceModels),然后存到数据库(blob); 项目里的应用端(用户手机),随机时间的调用摄像头,采集到被比对图片;并对该记录进行标记; 项目时比对端(服务器),定时询问数据库,哪些被标记记录需要比对,然后通过数据库记录,找到该图片,并通过ArcFace人脸检测,特征提取,获取到.dat被比对源(LocalFaceModels) 然后将这两个源在内存中进行比对,得分高于0.7的,就通过;
前两端就不多说了,都是一些常规的操作.重点讲下比对端(服务器);
先说我做的第一个版本,做的是一个控制台程序;
//首先定义了一个调用类;
MatchUserFace;它里边包含了初始化,人脸检测,特征提取,人脸比对,以及一些辅助方法;
//然后在Program里定义了一个委托,这个委托的作用,就是能够让我可以带参数进去ArcFace的检测与比对核心; public delegate bool MatchHandler(string userid, string studyid, string photoid, string photopath);
//最后我的Program里边,就是做一个递归,去不断的问数据库拿被标志需要进行核对的记录,拿到图片后,就进行比对;
QueryDataFile(string upstate); 下边这段就是在QueryDataFile();去实现异步调用比对核心; MatchHandler handler = new MatchHandler(MatchUserFace.GetAndMatchImage); string Identification = string.Format("USERID:{0} STUDYID:{1} PHID:{2}", userid, studyid, photoid); IAsyncResult result = handler.BeginInvoke(userid, studyid, photoid, pathstr, RecognizeEngine, DetectEngine, new AsyncCallback(CallbackFunc), Identification);
下边这段就是异步的结果回调; static void CallbackFunc(IAsyncResult result) { MatchHandler handler = (MatchHandler)((AsyncResult)result).AsyncDelegate; bool match = handler.EndInvoke(result); string strmatch = string.Empty; if (match) { strmatch = " 比对结果:OK"; } else { strmatch = " 比对结果:NO"; } Console.WriteLine(result.AsyncState + strmatch); GC.Collect(); } 写好了,发布到服务器上,还想着中午吃个鸡腿奖励下自己;不想…发布后不到两小时,小弟来说:服务器是不是出问题了,下边所有业务窗口访问速度严重延迟…立马跑到机房去看,一看没毛病呀,所有的服务都好好的,没有卦死…再打开资源监视器一看,靠…那个比对端一下吃3个多G的内存,而且还在不断上升中…立马停掉,然后再问小弟,下边业务是否正常,他回复正常了…那么说,就是我写的这个比对端有问题了!改!!! 第二个版本, 下了机房看代码…左看右看,没有哪不对呀,一步步按步就班的…毫无头绪时,就想,是不是服务器内存不够而已,打申请拿了64G回来.再开程序也是一样吃的很紧,但是下边业务窗口倒是不延时,看来内存增大还是有好处的…呵…;但是源头问题还是没解决,不行的呀!到了晚饭时,一道灵光拍进脑门,我看到代码里我是每异步调用一次,就初始化一次ArcFace的SDK.我就想,是不是这个原因导致呢?修改方法,去试试!! //把那个委托改成如下:
public delegate bool MatchHandler(string userid, string studyid, string photoid, string photopath, IntPtr RecognizeEngine, IntPtr DetectEngine); //然后初始化SDK放到了Program里做: string appId = "4yHjnxK94FCK6L7HaJieWawSLubnANXXXXX"; string sdkFDKey = "7S6Xp4mtroLnjTt7qDYnd2dqHXXXXX"; string sdkFRKey = "7S6Xp4mtroLnjTt7qDYnd2dxSgXXXXX"; int retCode = AFDFunction.AFD_FSDK_InitialFaceEngine(appId, sdkFDKey, pMem, detectSize, ref DetectEngine, 5, nScale, nMaxFaceNum); int retCode2 = AFRFunction.AFR_FSDK_InitialEngine(appId, sdkFRKey, pMemRecongnize, detectSize, ref RecognizeEngine); //最后把异步调用的方法改成如下:
MatchHandler handler = new MatchHandler(MatchUserFace.GetAndMatchImage); string Identification = string.Format("USERID:{0} STUDYID:{1} PHID:{2}", userid, studyid, photoid); IAsyncResult result = handler.BeginInvoke(userid, studyid, photoid, pathstr, RecognizeEngine, DetectEngine, new AsyncCallback(CallbackFunc), Identification); 再次发布到服务器.然后再到资源监视器去看,哟…线程数不高了而且增长的还不快…好开心!!以为搞好了;就回宿舍睡觉去了!!不想…睡得迷糊的时候,我们的客服小妹妹的电话就打到我这了,我说什么事,她说现在大面积反映用户比对不了?what?我说不可能吧,是不是当地电信故障呀?我自己拿手机试了下,真的不行呀!!!快速赶回办公室远程看了下服务器,我的乖乖…比对端卦了!!!我再看日志,日志没有捕捉到程序异常,只是捕到了个:Value cannot be null.Parameter name: source;我吃你大米了,我刨你家玉米地了,为啥要这么对我!重启比对端,然后都可以正常运作了…我决定在这监视这个比对端,在资源监视器我到是发现了一个:w3wp.exe它在不断的涨内存(这是要划重点的)想想这可已经是深夜了.果不出其然,运行了大概两个多小时后,程序又卦了.我的乖乖,为啥会这样呢,一时半会也想不出办法呀!我也总不能呆在服务器旁它停了,我就重启吧! 第二天致电虹软,反映了程序会运行一段时间就会卦掉,虹软这边也提出了很多宝贵意见, 1.先着眼把捕捉到的那个错误,查出来,看看是否处理好了,程序还会不会卦;那我就在程序里增加了日志打印,还真就发现了几个在DEMO里没有处理到的问题: <1>每个Marshal.AllocHGlobal,用完以后,一定要释放; <2>AFD_FSDK_StillImageFaceDetection;AFR_FSDK_ExtractFRFeature;这两个函数要判断返回值是否等于0; 所以 MatchUserFace 调用类我作了如下修改:
private static byte[] detectAndExtractFeature(Image imageParam, out Image facerect, IntPtr RecognizeEngine, IntPtr DetectEngine) { byte[] feature = null; facerect = null; try { int width = 0; int height = 0; int pitch = 0; Bitmap bitmap = new Bitmap(imageParam); byte[] imageData = getBGR(bitmap, ref width, ref height, ref pitch); IntPtr imageDataPtr = Marshal.AllocHGlobal(imageData.Length); Marshal.Copy(imageData, 0, imageDataPtr, imageData.Length); ASVLOFFSCREEN offInput = new ASVLOFFSCREEN(); offInput.u32PixelArrayFormat = 513; offInput.ppu8Plane = new IntPtr[4]; offInput.ppu8Plane[0] = imageDataPtr; offInput.i32Width = width; offInput.i32Height = height; offInput.pi32Pitch = new int[4]; offInput.pi32Pitch[0] = pitch; AFD_FSDK_FACERES faceRes = new AFD_FSDK_FACERES(); IntPtr offInputPtr = Marshal.AllocHGlobal(Marshal.SizeOf(offInput)); Marshal.StructureToPtr(offInput, offInputPtr, false); IntPtr faceResPtr = Marshal.AllocHGlobal(Marshal.SizeOf(faceRes)); //人脸检测 int detectResult = AFDFunction.AFD_FSDK_StillImageFaceDetection(DetectEngine, offInputPtr, ref faceResPtr); if (detectResult == 0) { try { object obj = Marshal.PtrToStructure(faceResPtr, typeof(AFD_FSDK_FACERES)); faceRes = (AFD_FSDK_FACERES)obj; for (int i = 0; i < faceRes.nFace; i++) { MRECT rect = (MRECT)Marshal.PtrToStructure(faceRes.rcFace + Marshal.SizeOf(typeof(MRECT)) * i, typeof(MRECT)); int orient = (int)Marshal.PtrToStructure(faceRes.lfaceOrient + Marshal.SizeOf(typeof(int)) * i, typeof(int)); if (i == 0) { facerect = CutFace(bitmap, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); } } } catch (Exception ex) { LogNetWriter.Error("人脸检测时出错:" + ex.Message); } } if (faceRes.nFace > 0) { try { AFR_FSDK_FaceInput faceResult = new AFR_FSDK_FaceInput(); int orient = (int)Marshal.PtrToStructure(faceRes.lfaceOrient, typeof(int)); faceResult.lOrient = orient; faceResult.rcFace = new MRECT(); MRECT rect = (MRECT)Marshal.PtrToStructure(faceRes.rcFace, typeof(MRECT)); faceResult.rcFace = rect; IntPtr faceResultPtr = Marshal.AllocHGlobal(Marshal.SizeOf(faceResult)); Marshal.StructureToPtr(faceResult, faceResultPtr, false); AFR_FSDK_FaceModel localFaceModels = new AFR_FSDK_FaceModel(); IntPtr localFaceModelsPtr = Marshal.AllocHGlobal(Marshal.SizeOf(localFaceModels)); int extractResult = AFRFunction.AFR_FSDK_ExtractFRFeature(RecognizeEngine, offInputPtr, faceResultPtr, localFaceModelsPtr); if (extractResult == 0) { Marshal.FreeHGlobal(faceResultPtr); Marshal.FreeHGlobal(offInputPtr); object objFeature = Marshal.PtrToStructure(localFaceModelsPtr, typeof(AFR_FSDK_FaceModel)); Marshal.FreeHGlobal(localFaceModelsPtr); localFaceModels = (AFR_FSDK_FaceModel)objFeature; feature = new byte[localFaceModels.lFeatureSize]; Marshal.Copy(localFaceModels.pbFeature, feature, 0, localFaceModels.lFeatureSize); localFaceModels = new AFR_FSDK_FaceModel(); } } catch (Exception ex) { LogNetWriter.Error("提取特征时出错:" + ex.Message); } } bitmap.Dispose(); imageData = null; Marshal.FreeHGlobal(imageDataPtr); //Marshal.FreeHGlobal(faceResPtr); offInput = new ASVLOFFSCREEN(); faceRes = new AFD_FSDK_FACERES(); } catch (Exception ex) { LogNetWriter.Error("识别人脸并提取人脸特征出错:" + ex.Message); } return feature; } 当然了,比对的时候也作了一些修改,就是当比对完了以后,就做了指针释放; ``` Marshal.FreeHGlobal(firstFeaturePtr); Marshal.FreeHGlobal(secondFeaturePtr); Marshal.FreeHGlobal(firstPtr); Marshal.FreeHGlobal(secondPtr); ``` 经过这一次修改后,再发布到服务器,哟…不错哦…运行的时间久了…但还是会卦,而且那个w3wp.exe还是会不断的拉内存;这个版本的运行时间可以达到4小左右了;我就想总得有个解决办法吧;再次致电虹软,再次反映这个问题,虹软这边给我的建议就是不要去进行多线程,我想想也对,要把逻辑简单化,我就把识别核心打包成一个EXE.然后在Program里调用这个EXE.意思就是每当我有需要识别的图片,我就调一个EXE.然后EXE处理完以后,就自我释放了… 于是我改了第三版: //这里就是一条线程在做处理 ``` string strmatch = string.Empty; ControlExeClass _ControlExeClass = new Model.ControlExeClass(); //这个方法是调一个EXE,EXE的内容是:ControlExeClass.cs; //做的任务就是把图片进行人脸检测,人脸特征提取,人脸识别; bool bo = _ControlExeClass.ControlExe(userid, studyid, photoid, pathstr); if (bo) { iCheck_OK++; label5.Text = iCheck_OK.ToString(); strmatch = " 比对结果:OK"; } else { strmatch = " 比对结果:NO"; } string dates = " 比对时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); textBox1.Text += " USERID:" + userid + " STUDYID:" + studyid + " PHID:" + photoid + strmatch + dates + Environment.NewLine; ``` 然后那个EXE就是沿用MatchUserFace调用类,在EXE的主线程里完成调用;还别说,用了这个方法后,内存不拉升了,而且w3wp.exe上涨,也只是在EXE工作的一刹那上来,EXE干完活后,它就会生成一个新的w3wp.exe,旧的w3wp.exe那个会被注销掉…哗…想想就开心,终于如愿解决了问题,但…当一个人觉得越顺利时,往往大麻烦就会来了.正如我觉得上天不会对我那么好一样,运行了大概一天后,程序还是卦了.苍天呀,大地呀,我到底做错了什么… 正在我一筹莫展时,我就老记恨这个w3wp.exe,到底是什么东东,好,度娘下彻底了解下它. 度娘是这么形容它的: w3wp.exe是在IIS(因特网信息服务器)与应用程序池相关联的一个进程,如果你有多个应用程序池,就会有对应的多个w3wp.exe的进程实例运行。这个进程用来分配大量的系统资源。 好,既然说我的IIS里的应用程序池,那我就对我的应用程序池进行固定内存回收不就好了嘛;我就对线程池做了一个固定内存回收,当达到400000KB时就做一次回收. 这一下设置做下去后,的确是立竿见影的,当EXE工作时w3wp.exe就从来没高过400000KB;我想这一下应该彻底解决了吧;可是…程序还是卦了…我是真的不得上天倦顾呀… 一连几天毫无头绪,胡子长一脸了,也没心思刮,领导这边还想刮我骨头呢…唉…上下压力都好大呀.搞得我肚子也不舒服,就去厕所蹲了个坑,还别说,这个坑,含金量特高.又一道灵光打进了我的脑门,我想呀,是不是我的递归出现了问题呢???我就回去看了下代码,我的递归逻辑是没有问题的呀,一步步有板有眼,这是怎么回事呢,我又度娘了下,关于C#的递归,是这么形容的:一个算法中,由于递归调用次数过多,堆栈是会溢出。递归使用的内存大小累计达4G,系统就会进行内存回收. 至于何时收,怎么收,就是windows的事情了.乖乖…既然有这么一个限定,我不用不就好了嘛,我就用死询还不好吗? 所以第4版修改如下: ``` private static void CycleData() { while (true) { if (_DoWork) { break; } else { QueryDataFile("U"); Thread.Sleep(1500); } Thread.Sleep(2000); } } ``` 至此所有问题解决!!!哦…忘说了,我后来没有单独调用EXE这种方法了,改成了第一版的控制台程序,其结果是一样的; 现在…呵呵…我可是十分轻松着座在大班椅上,喝着奶茶,身边座着小秘,我说,她打的这篇文章…呵…开玩笑了,文章里每个字都是我自己亲手敲的,同时也十分感谢虹软能提供这么优秀的SDK供我使用,更要感谢虹软的技术支持,给我莫大的帮助; 最后总结几点: 1.SDK可以只初始化一次,然后ref传参进结构体,就可以一直用下去; 2.每个Marshal.AllocHGlobal,用完以后,一定要释放; 3.可以异步回调进行; 4.AFRFunction.AFR_FSDK_ExtractFRFeature; AFDFunction.AFD_FSDK_StillImageFaceDetection; 这两个函数要判断返回值是否等于0; 5.最最最重要一点,严禁使用递归去调用;宁愿用死询代替;(因为这个就是导致我程序死掉的主因),因为递归要是深度太大,而且次数过多,累计内存使用达4G以上,系统就会做一次线程与内存回收,至于怎么收,何时收就是不定时的,所以一定不要用递归,这个是我在C#官方看到对于递归的解释; 6.如果是使用windows服务器进行虹软SDK的;建议IIS线程池做一个固定内存回收机制; 最后上传一下几个示例片段吧,因为个中涉及到一些数据库操作,我整个工程就不上传了:
人工智能
2019-04-04 16:45:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
虹软的人脸识别技术也是很强的,重要的是他免费提供了离线的sdk,还提供了实例,这个是目前几家研究人脸识别的大公司里面少有的。识别能力正常用还是可以的。我这个代码是调用的离线sdk实现的 from arcsoft import CLibrary, ASVL_COLOR_FORMAT, ASVLOFFSCREEN,c_ubyte_p,FaceInfo from arcsoft.utils import BufferInfo, ImageLoader from arcsoft.AFD_FSDKLibrary import * from ctypes import * import traceback import cv2 import time APPID = c_char_p(b'your id') FD_SDKKEY = c_char_p(b'your key') FD_WORKBUF_SIZE = 20 * 1024 * 1024 MAX_FACE_NUM = 50 bUseYUVFile = False bUseBGRToEngine = True def doFaceDetection(hFDEngine, inputImg): #对图像中的人脸进行定位 faceInfo = [] pFaceRes = POINTER(AFD_FSDK_FACERES)() ret = AFD_FSDK_StillImageFaceDetection(hFDEngine, byref(inputImg), byref(pFaceRes)) #ret 为0 if ret != 0: print(u'AFD_FSDK_StillImageFaceDetection 0x{0:x}'.format(ret)) return faceInfo faceRes = pFaceRes.contents print('******') facecont=faceRes.nFace #faceRes 是一个对象所以 输出会是一个地址值 而他的一个属性nface是表示的是人脸的个数 print('%d 个人脸' %facecont) if faceRes.nFace > 0: for i in range(0, faceRes.nFace): rect = faceRes.rcFace[i] orient = faceRes.lfaceOrient[i] faceInfo.append(FaceInfo(rect.left,rect.top,rect.right,rect.bottom,orient)) return faceInfo def loadImage(filePath): inputImg = ASVLOFFSCREEN() if bUseBGRToEngine: #true bufferInfo = ImageLoader.getBGRFromFile(filePath) inputImg.u32PixelArrayFormat = ASVL_COLOR_FORMAT.ASVL_PAF_RGB24_B8G8R8 inputImg.i32Width = bufferInfo.width inputImg.i32Height = bufferInfo.height inputImg.pi32Pitch[0] = bufferInfo.width*3 inputImg.ppu8Plane[0] = cast(bufferInfo.buffer, c_ubyte_p) inputImg.ppu8Plane[1] = cast(0, c_ubyte_p) inputImg.ppu8Plane[2] = cast(0, c_ubyte_p) inputImg.ppu8Plane[3] = cast(0, c_ubyte_p) else: bufferInfo = ImageLoader.getI420FromFile(filePath) inputImg.u32PixelArrayFormat = ASVL_COLOR_FORMAT.ASVL_PAF_I420 inputImg.i32Width = bufferInfo.width inputImg.i32Height = bufferInfo.height inputImg.pi32Pitch[0] = inputImg.i32Width inputImg.pi32Pitch[1] = inputImg.i32Width // 2 inputImg.pi32Pitch[2] = inputImg.i32Width // 2 inputImg.ppu8Plane[0] = cast(bufferInfo.buffer, c_ubyte_p) inputImg.ppu8Plane[1] = cast(addressof(inputImg.ppu8Plane[0].contents) + (inputImg.pi32Pitch[0] * inputImg.i32Height), c_ubyte_p) inputImg.ppu8Plane[2] = cast(addressof(inputImg.ppu8Plane[1].contents) + (inputImg.pi32Pitch[1] * inputImg.i32Height // 2), c_ubyte_p) inputImg.ppu8Plane[3] = cast(0, c_ubyte_p) inputImg.gc_ppu8Plane0 = bufferInfo.buffer return inputImg if __name__ == u'__main__': t=time.time() print(u'#####################################################') # init Engine pFDWorkMem = CLibrary.malloc(c_size_t(FD_WORKBUF_SIZE)) hFDEngine = c_void_p() ret = AFD_FSDK_InitialFaceEngine(APPID, FD_SDKKEY, pFDWorkMem, c_int32(FD_WORKBUF_SIZE), byref(hFDEngine), AFD_FSDK_OPF_0_HIGHER_EXT, 32, MAX_FACE_NUM) #ret 为0 if ret != 0: CLibrary.free(pFDWorkMem) print(u'AFD_FSDK_InitialFaceEngine ret 0x{:x}'.format(ret)) exit(0) #--------------------------------以上部分两个函数以及主函数的几条语句不变----------------------------------------------------------- filePath = '001.jpg' inputImg = loadImage(filePath) #调用loadImage函数 返回一种格式(目前还不知道这种格式是什么) frame=cv2.imread(filePath) # do Face Detect faceInfos = doFaceDetection(hFDEngine, inputImg) #调用dofaceDetection函数 进行图像处理检测人脸 #print('faceInfos %s'% faceInfos[0]) for i in range(0, len(faceInfos)): rect = faceInfos[i] print(u'{} ({} {} {} {}) orient {}'.format(i, rect.left, rect.top, rect.right, rect.bottom, rect.orient)) cv2.rectangle(frame, (rect.left, rect.top), (rect.right, rect.bottom), (0, 0, 255), 2) cropimg=frame[rect.top:rect.bottom,rect.left:rect.right]# 使用opencv裁剪照片 把人脸的照片裁剪下来 cv2.imwrite('crop-photo/'+str(i)+'.jpg',cropimg) # 把人脸照片保存下来 AFD_FSDK_UninitialFaceEngine(hFDEngine) # release Engine cv2.imshow('tuxiang',frame) cv2.waitKey(1) print('所用时间为{} '.format(time.time()-t)) #不进行保存图片 0.12s 保存图片0.16s time.sleep(1) CLibrary.free(pFDWorkMem) print(u'#####################################################')
运行结果
运行时间0.14800000190734863   
底层是c写的所以运行起来还是比较快的  使用的是离线的sdk配置需要动态链接库fd (官网有)
对于虹软的这个 我只会用 里面的代码很大一部分都是不懂的,因为那些函数都被封装起来了,定义看不到也看不懂。
 opencv就是用来显示照片以及标框  time用来测时间和暂停
对于虹软的人脸识别,是使用了另一种动态链接库fr,跟这个类似,代码有些差别,等做出来基于虹软的实时的人脸识别再分享出来。
人工智能
2019-04-04 16:12:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
    最近公司需要做一个人脸检测的新功能,在网上找了找,有很多开源的第三方库都可以用,例如OpenCV,虹软,Face++,百度,阿里等等。
    由于在APP的需求,只能本地检测,所以Face++,百度,阿里这些需要用HTTP进行网络请求返回结果的,只能舍弃了。集中研究OpenCV以及虹软。
    首先介绍一下虹软,这家公司开源了so库以及jar包,可直接下载集成到项目中,简单配置之后就可检测人脸,而且识别率还是不错的。可借鉴此博客点击打开链接。详细教你在Android Studio中使用虹软检测以及识别人脸。
    接下来讲一下使用OpenCV开源库检测人脸。其实OpenCV非常强大,有兴趣的同学可以去查阅一下。目前只讲一下使用OpenCV通过Camera动态检测人脸。OpenCV搭建流程可百度,内容很多,这里仅做简单说明。
    首先下载OpenCV4Android Demo,新建项目等操作省略,然后倒入OpenCV Samples中的face_detection项目,使用NDK编译检测的so库,倒入OpenCV SDK中的java Module到项目中,在app/src/main目录下新建jniLibs,复制sdk/native/libs/armeabi-v7a/libopencv_java3.so到jniLibs/armeabi-v7a中(可多选,arm,x86等,由于我只需要v7a即可,只倒入这个),复制sdk/native/jni/include到jniLibs下。可直接下载代码OpenCVJ。
    到此搭建完工程,点击运行,发现只有横屏下才能正确检测到人脸,但是项目需求是在竖屏下检测人脸,怎么办,接着寻找答案,发现这篇博客OpenCV on Android 开发 (4)竖屏预览图像问题解决方法-续,在此多谢这位兄台的先驱行动,使用Core.rotate函数,最后一个参数填入Core.ROTATE_90_CLOCKWISE,旋转Gray Mat后可正确检测到人脸,返回MatOfRect,再把MatOfRect放入到mRgba中,再次通过Core.rotate函数,但是最后一个参数需填入Core.ROTATE_90_COUNTERCLOCKWISE,再返回到CameraBridgeViewBase中的deliverAndDrawFrame中,经过此操作后可以正确显示出人脸检测框。
    使用上述方法就基本完成了。按照惯例,文章没有写完,肯定会有但是的,没错,这里也有。
    但是:实际使用时,发现帧率只有10帧左右,完全无法接受,整个页面都是卡顿的,怎么办,接着寻找方法。想到一个idea,既然是检测,我只需要OpenCV的检测功能,不需要OpenCV来自己画图,直接使用camera的预览效果,我只把人脸检测框画到预览图上面去就好,这样可以保证预览不卡顿,只是检测框可能要一点时间才能显示,这也是无法避免的了。
    首先看xml文件: `
人工智能
2019-04-04 15:49:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
  环境: win10(10.0.16299.0)+ VS2017
sdk版本:ArcFace v2.0
OPENCV3.43版本 x64平台Debug、Release配置都已通过编译
下载地址: https://download.csdn.net/download/cngwj/10763108
配置过程
->0x01 下载sdk:
虹安sdk https://ai.arcsoft.com.cn
->0x02 工程配置:
1、 添加工程的头文件目录:
a) 右键单击工程名, 选择属性---配置属性---c/c++---常规---附加包含目录
b) 添加头文件存放目录
2、 添加文件引用的 lib 静态库路径:
a) 右键单击工程名,选择属性---配置属性---链接器---常规---附加库目录
b) 添加 lib 文件存放
3、 添加工程引用的 lib 库:
a) 右键单击工程名,选择属性---配置属性---链接器---输入---附加依赖项
b) 添加依赖的 lib 库名称
4、自定义可执行文件输出目录
5、 添加工程引用的 dll 动态库:
a) 把引用的 dll 放到工程的可执行文件所在的目录下(复制到Build目录)
6、添加自己申请的APPID
->0x03 参考代码 /************************************************************************ * Copyright(c) 2018 * All rights reserved. * File: samplecode.cpp * Brief: Powered by ArcSoft 环境: win10(10.0.16299.0)+ VS2017 sdk版本:ArcFace v2.0 x64平台Debug、Release配置都已通过编译 * Version: 0.1 * Author: 一念无明 * Email: cngwj@outlook.com * Date: 2018.11.3 * History: 2018.11.3 建立项目 ************************************************************************/ #include "pch.h" #include "arcsoft_face_sdk.h"//接口文件 #include "amcomdef.h"//平台文件 #include "asvloffscreen.h"//平台文件 #include "merror.h"//错误码文件 #include //目录操作 #include #include #include #include using namespace std; using namespace cv; #pragma comment(lib, "libarcsoft_face_engine.lib") #define APPID "" #define SDKKey "" #define MERR_ASF_BASE_ALREADY_ACTIVATED 90114 //SDK已激活 #define SafeFree(p) { if ((p)) free(p); (p) = NULL; } #define SafeArrayDelete(p) { if ((p)) delete [] (p); (p) = NULL; } #define SafeDelete(p) { if ((p)) delete (p); (p) = NULL; } int main() { //激活SDK MRESULT res = ASFActivation(APPID, SDKKey); if (MOK != res && MERR_ASF_BASE_ALREADY_ACTIVATED != res) printf("ALActivation fail: %d\n", res); else printf("ALActivation sucess: %d\n", res); //初始化引擎 MHandle handle = NULL; MInt32 mask = ASF_FACE_DETECT | ASF_FACERECOGNITION | ASF_AGE | ASF_GENDER | ASF_FACE3DANGLE; res = ASFInitEngine(ASF_DETECT_MODE_IMAGE, ASF_OP_0_ONLY, 16, 5, mask, &handle); if (res != MOK) printf("ALInitEngine fail: %d\n", res); else printf("ALInitEngine sucess: %d\n", res); // 人脸检测 IplImage* img = cvLoadImage("../Build\\1.bmp");//图片宽度需符合4的倍数 IplImage* img1 = cvLoadImage("../Build\\2.bmp"); if (img && img1) { ASF_MultiFaceInfo detectedFaces1 = { 0 };//多人脸信息; ASF_SingleFaceInfo SingleDetectedFaces1 = { 0 }; ASF_FaceFeature feature1 = { 0 }; ASF_FaceFeature copyfeature1 = { 0 }; res = ASFDetectFaces(handle, img->width, img->height, ASVL_PAF_RGB24_B8G8R8, (MUInt8*)img->imageData, &detectedFaces1); if (MOK == res) { SingleDetectedFaces1.faceRect.left = detectedFaces1.faceRect[0].left; SingleDetectedFaces1.faceRect.top = detectedFaces1.faceRect[0].top; SingleDetectedFaces1.faceRect.right = detectedFaces1.faceRect[0].right; SingleDetectedFaces1.faceRect.bottom = detectedFaces1.faceRect[0].bottom; SingleDetectedFaces1.faceOrient = detectedFaces1.faceOrient[0]; //单人脸特征提取 res = ASFFaceFeatureExtract(handle, img->width, img->height, ASVL_PAF_RGB24_B8G8R8, (MUInt8*)img->imageData, &SingleDetectedFaces1, &feature1); if (res == MOK) { //拷贝feature copyfeature1.featureSize = feature1.featureSize; copyfeature1.feature = (MByte *)malloc(feature1.featureSize); memset(copyfeature1.feature, 0, feature1.featureSize); memcpy(copyfeature1.feature, feature1.feature, feature1.featureSize); } else printf("ASFFaceFeatureExtract 1 fail: %d\n", res); } else printf("ASFDetectFaces 1 fail: %d\n", res); //第二张人脸提取特征 ASF_MultiFaceInfo detectedFaces2 = { 0 }; ASF_SingleFaceInfo SingleDetectedFaces2 = { 0 }; ASF_FaceFeature feature2 = { 0 }; res = ASFDetectFaces(handle, img1->width, img1->height, ASVL_PAF_RGB24_B8G8R8, (MUInt8*)img1->imageData, &detectedFaces2); if (MOK == res) { SingleDetectedFaces2.faceRect.left = detectedFaces2.faceRect[0].left; SingleDetectedFaces2.faceRect.top = detectedFaces2.faceRect[0].top; SingleDetectedFaces2.faceRect.right = detectedFaces2.faceRect[0].right; SingleDetectedFaces2.faceRect.bottom = detectedFaces2.faceRect[0].bottom; SingleDetectedFaces2.faceOrient = detectedFaces2.faceOrient[0]; res = ASFFaceFeatureExtract(handle, img1->width, img1->height, ASVL_PAF_RGB24_B8G8R8, (MUInt8*)img1->imageData, &SingleDetectedFaces2, &feature2); if (MOK != res) printf("ASFFaceFeatureExtract 2 fail: %d\n", res); } else printf("ASFDetectFaces 2 fail: %d\n", res); // 单人脸特征比对 MFloat confidenceLevel; res = ASFFaceFeatureCompare(handle, ©feature1, &feature2, &confidenceLevel); if (res != MOK) printf("ASFFaceFeatureCompare fail: %d\n", res); else printf("ASFFaceFeatureCompare sucess: %lf\n", confidenceLevel); // 人脸信息检测 MInt32 processMask = ASF_AGE | ASF_GENDER | ASF_FACE3DANGLE; res = ASFProcess(handle, img1->width, img1->height, ASVL_PAF_RGB24_B8G8R8, (MUInt8*)img1->imageData, &detectedFaces1, processMask); if (res != MOK) printf("ASFProcess fail: %d\n", res); else printf("ASFProcess sucess: %d\n", res); // 获取年龄 ASF_AgeInfo ageInfo = { 0 }; res = ASFGetAge(handle, &ageInfo); //printf("年龄: %d\n", ageInfo); if (res != MOK) printf("ASFGetAge fail: %d\n", res); else printf("ASFGetAge sucess: %d\n", res); // 获取性别 ASF_GenderInfo genderInfo = { 0 }; res = ASFGetGender(handle, &genderInfo); if (res != MOK) printf("ASFGetGender fail: %d\n", res); else printf("ASFGetGender sucess: %d\n", res); // 获取3D角度 ASF_Face3DAngle angleInfo = { 0 }; res = ASFGetFace3DAngle(handle, &angleInfo); if (res != MOK) printf("ASFGetFace3DAngle fail: %d\n", res); else printf("ASFGetFace3DAngle sucess: %d\n", res); SafeFree(copyfeature1.feature); //释放内存 } //获取版本信息 const ASF_VERSION* pVersionInfo = ASFGetVersion(handle); printf("版本号: %s\n", pVersionInfo->Version); //反初始化 res = ASFUninitEngine(handle); if (res != MOK) printf("ALUninitEngine fail: %d\n", res); else printf("ALUninitEngine sucess: %d\n", res); getchar(); return 0; } ```
人工智能
2019-04-04 15:17:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
背景
  在 《UI2CODE--整体设计》 篇中,我们提到UI2Code工程的整体流程。前步图片分析之后,我们可以得到对应的DSL布局描述。利用DSL的资讯,结合IntelliJ Plugin介面工具,面向使用者提供生成对应Flutter代码。
  本篇主要介绍我们如何处理DSL的资讯,想法上即是Flutter的翻译机。总体概念如下:
输入的DSL是什么?
  DSL做为一种描述语言,抽象表示为了解决某一类任务而专门设计的计算机语言。在此我们的DSL代表图像识别和布局识别侧的输出,为一JSON格式。
  这些资讯主要描述了这个图层(Layer)的范围(Frame)、是什么样子的类型(Type)、是什么样子的样式(Styles)、含有哪些数据(Value)等等。图层集(Layers)栏位则代表了这张视觉稿的所有图层。
核心思路
  本节的目标是将DSL翻译成目标的Flutter代码。我们首先需要理解的是分散的图层间的关系,可能会有交叠、可能是并列排版。知道了关系之后,需想办法转化成Flutter widget的视图,根据此视图来生产对应代码。
  架构上我们把DSL tree和Flutter tree的建立,分拆为两个独立的分界。这样比较容易定义问题,并且保持弹性。如果今天的目标语言换成Weex或是iOS UI,我们就只需要更动代码翻译的模组。
第一把刀:DSL tree建立
  上图的左侧代表了来源DSL的layers资料,代表者一个一个的图层。右侧是目标的DSL Tree,这棵树的结构上明确叙述了图层之间的包裹、交叠等关系。并且包含了某些特殊关系的节点聚合。
  作法上利用每个Layer的Frame,以及所属的类别(文字、图像、容器),利用下面的规则组合树的关系: 图层之间的包裹关系,例如某些图层为容器,代表下面是可以挂其他节点的(这边带有背景属性的容器,我们定义称之为 Shape ) 区块式组件( Block , 如ListView/GridView)。可以将图层组成View item的关系 闲鱼定义的组件资讯(如 CI 以及 BI ),这部份非闲鱼工程可以忽略 重复布局( Repeat )的资讯,将相同的图层归类合并,目的为简化树
  根据以上我们采用了分层,由大至小的次序将Layer分群合并。另外,在合并时layer之间彼此可能有关联;它们可能同属于Block,也可能同属于某个Repeat。 所以对于上面定义的Repeat、BI、Block、CI、Shape都可能有交错的嵌套关系,这是必须要处理的部份 。
第二把刀:Flutter tree建立
  在Flutter Tree的建构中,核心概念先处理布局。布局的概念如剥洋葱一般,我们先去除四周的padding,然后以人类视觉layout的直觉先尝试 横切分 ,再进行 竖切分 。
1.先剥洋葱去除padding
2.接著我们的算法会先尝试是否可以横切,如下图我们可以切割成为Row1/ Row2
3.针对Row1在尝试再进行竖切,如下图可以得到Column1/ Column2/ Column3
  根据以上切分的规则,我们就可以定义出如Row、Column、Padding的几个节点,以及它们的Parent/ Child关系。将DSL tree同一层的节点做切分,一边切分一边建立Flutter node,遍历完整颗DSL,即可得到粗略的Flutter tree关系。
= 无法切分时的处理
  当图层切分不开时,这时候就要使用绝对布局叠层的概念,这个概念在Flutter内称之为Stack。
  多个图层在DSL tree的关系为兄弟节点,根据此些图层的Frame,我们判断出来它们是彼此相交的,我们会以Z-order概念,来决定上下交叠的关系。最后,这些图层将组成一个新Stack节点,并且产生此节点的Frames为此些图层覆盖的范围。
= 针对文字的进阶处理
  基本上交叠的图层以Stack的处理就可以正确显示,但在文字图层上可能含有误区。
  如上图因为文字本身的上下左右是含有padding的,在我们图层的识别时,可能会计算出彼此的frame是交叠的,但实际上UI希望它们并不是Stack关系。
  为了解决这个问题,我们引入了一个oriFrame的概念,用文字最原始的像素当做是oriFrame。所以遇到为文字的图层时,我们会先判断本身的oriFrame是否交叠,如果是的话才采用Stack切割,否则就以此oriFrame对原始的frame做修正。
文字还有什么特性?
  另外,因为文字的内容通常是动态的,所以拥有了”所见不一定为所得”的特性。这些特性主要包含了是否该换行、内容区域是否可以拉伸、文字Padding等,这些特性都会影响到我们的布局。
  以下图为例,我们在处理Layout时肉眼很明显可以知道这些特徵。文字的行数我们可以以 视觉稿当做最大显示范本 ,文字区域的宽度部分,则需要特别判断哪些区域是可以被拉伸的。
确立文字范围
  在决定拉伸对象之前,我们需要定义哪些widget是将内容完整显示,不能被拉伸的:如图片、Container容器、Stack区域、Component组件
  接著处理的流程如下: 首先判断所有Child内是否有多行文字或宽度固定的文字,如果是的话针对其处理。需要加上Flex。 若无以上的状况,则判断Child间的Padding关系 如果可拉伸的widget的Padding大于平均值的个数有多个,则这些都加上Flex 如果只有单个时,则找寻最大Padding的widget(使用分群拉伸算法) 最后,但当Row里面存有拉伸的状况时,需要把Row的最后一个child加上Right padding,否则拉伸元素会填满父容器。
分群拉伸算法:这个算法的目的是找到最佳拉伸的对象。我们的思考上将Widget做分组,分组后判断整体的Alignment(如左右对齐)或是拉伸关系。若在拉伸状况下,判断适合让哪个组别拉伸,在进一步判断适合让组别的内部元件拉伸。
  举例如下为一个Row排列的控件,其中排列为Image、CI、Text1、Text2、Text3:
  依据Widget之间的距离,在上图分为了Group1及Group2两个群体。先以Group1判断是否存在可拉伸的对象, 接著才判断Group2。所以这5个Widget分别得到了3, 2, 1, 4, 5的优先级。以本例而言,Text1为最高优先,而且其为可拉伸的,故决定将Flex属性加于此。
  在Expanded的处理上,是我们目前遇到最大的困难点,甚至人工判断都可能有歧义。上面的规则是我们归纳出众多视觉稿的通解,但不能100%完全解决问题。所以这部份判断错误的部分,我们期待在Plugin的交互中使用人工解决。
= 判断Alignment优化
  以上的处理已经可以正确生成Flutter tree,但是我们想进一步地将Flutter代码更加优雅。在此我们针对了三种元件的Alignment做了处理,分别是Container、Row、Column,其概念都是分析内部元件的padding关系,决定为居左、居中、或是居右对齐。
  举例如Column内部的children我们去判断左右的padding是否相等。若是则移除其padding,并且加上crossAxisAlignment为center。
  针对Row/ Container我们则会判断crossAxisAlignment(垂直方向)以及mainAxisAlignment(水平方向)。水平部份,这边我们采用更精细的方法,我们利用欧式距离建立一个 非监督算法 ,计算views是更为接近哪一个(居左、居中、居右)。算法这边先不详述,之后再以篇幅介绍。
最后:生成Flutter代码
  经过前面的步骤后,最终我们产生了一个Flutter Tree。生成时在节点的定义上,我们分为了两种,分别是View与Layout,以是否可以拥有Child为区别。以下是我们针对Flutter Tree所定义的部份类别:
  在节点的定义中,皆存储了各节点的Parent、Child属性。根据这些关系,我们定义每个节点的代码样板,例如FColumn对应的样板为:
new Column( #{alignment}, children: [ #{children}, ]
),
  最后我们以Root widget开始遍历整颗树,将每个节点所生成的Flutter代码结合,这样我们就可以得到整个Widget tree的代码了。
数据分离
  为了更好的重复利用生成代码,我们把生成的代码和数据再进一步做分离。分离后输出分为代码区以及Data model数据区:
  我们切割这些区域的目的为简化Widget tree直观上的代码复杂度,以及将数据抽离,让资料可由外部呼叫传入,以达成动态性。
整体架构回顾
  总合以上的概念,工程的细部架构如下:
  前面所说的针对文字以及Alignment的处理,在这边我们设计了一个工厂模式,如上图中经过Flutter Tree Builder后,我们可以去遍历整颗Widget tree,在工厂中判断判断符合条件的规则,经过处理去震荡优化原本的Widget tree。在这边未来我们可以不断地加上合适的规则,让Widget tree更加优化。
  整体架构使用静态分析的方法,读到此各位可能会有疑问:一些如动态的事件、View的Visibility、Input输入文字框等怎么处理?由于这些动态性在静态分析下无法解决,所以我们增强了Plugin上的编辑性,使用者只要勾选某些属性,即会在生成代码时自动判断,在Flutter自动增加对应的逻辑。以弥补静态图无法处理的问题。
  由于UI的灵活性高,十个人写的代码可能有十种不同风格。并且在分析上游的UI2DSL,以及Flutter代码的翻译,某些部份的精确性取决于我们的样本的认知,是否能够在有限的样本内观查出泛化的定律,分析上还是存有很多挑战性。
结合落地业务
  在整个UI2CODE的效果中,大约七成以上的页面都可以正确分析出来,剩下的是一些小细节如文字的处理等,基本上我们工具都能够将大框架的处理好,使用者可能只需微小的调整。
  UI2CODE案子在内部团队上线后,已经在闲鱼APP内的"玩家页面"采用了自动化生成的代码。在采用自动化工具后,大约减少了三分之二的UI开发时间(因初期还在熟悉工作流程,未来相信可以更快速)。同时,若在客户端大量采用我们工具,还可以让团队的代码结构有一些的规范,让生成工具来规范Widget UI以及Data Binding的框架,一致性以及后续的维护,相信是一个很大的诱因。
  并且闲鱼团队近期计画开发一款新的APP,在初期时能够快速开发UI,也将采用我们的工具。期望有更多的业务和经验积累。
后续计画
  近期我们推出了第一版UI2CODE,先计画于内部团队使用,利用使用的经验,让我们在叠代之下不断提高准确性。并且,我们正在调研结合NLP以及AST(语法树)的可能性,希望能够产出更有质量的代码。
  我们也期望未来能将此工具开放于Flutter community,对于推动整个Flutter技术有所推进。希望能让更多人跟我们一起找寻更有效率的写代码方法,如果有任何想法欢迎与我们交流,我们也持续不断地在进化工具中,谢谢各位的阅读!
作者:闲鱼技术-上叶,余晏
原文链接
​本文为云栖社区原创内容,未经允许不得转载。
人工智能
2019-04-04 10:55:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
在3月21日的2019阿里云峰会·北京上,阿里云发布新产品SaaS加速器:人工智能、虚拟现实等技术能力被集成为模块,ISV和开发者只要简单拖拽,就可以快速搭建SaaS应用。
发布现场,阿里云智能产品管理部总经理马劲进行简单演示。通过SaaS加速器仅用五天就开发完成了一款智能购车应用,并具备虚拟试驾等功能。过去,搭建这样一个智能购车应用,可能需要几十人的团队耗费一个月才能完成。
马劲表示,阿里云自己不做SaaS,要让大家来做更好的SaaS;要练好“内功”,更广泛、更深度地“被集成”。
实现SaaS业务规模化的关键在于拥有一个稳定强大的平台提供支撑、出色的前台提高开发定制效率,以及可沉淀和复用的中台能力。
此次阿里云推出的SaaS加速器,涵盖商业中心、能力中心、技术中心三大板块,是阿里巴巴商业、能力和技术的一次合力输出:技术能力在这里沉淀为一个个模块,ISV和开发者只要通过简单的操作,写很少的代码、甚至不写代码,就可以快速搭建一个SaaS应用,实现规模化、快速复制。
同时,借助阿里巴巴的丰富生态,ISV和开发者能够快速完成应用发布,向目标用户提供服务,形成从产品研发到部署交付的完整商业闭环。
作者:茜莹
原文链接
本文为云栖社区原创内容,未经允许不得转载。​
人工智能
2019-03-29 11:18:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
繁简转换
HanLP几乎实现了所有我们需要的繁简转换方式,并且已经封装到了HanLP中,使得我们可以轻松的使用,而分词器中已经默认支持多种繁简格式或者混合。这里我们不再做过多描述。
说明 :
·HanLP能够识别简繁分歧词,比如打印机=印表機。许多简繁转换工具不能区分“以后”“皇后”中的两个“后”字,HanLP可以。
算法详解 :
·《汉字转拼音与简繁转换的Java实现》——请查阅此文 from pyhanlp import * # 繁简转化 print(HanLP.convertToTraditionalChinese("“以后等你当上皇后,就能买草莓庆祝了”。发现一根白头发")) print(HanLP.convertToSimplifiedChinese("憑藉筆記簿型電腦寫程式HanLP")) # 简体转台湾繁体 print(HanLP.s2tw("hankcs在台湾写代码")) # 台湾繁体转简体 print(HanLP.tw2s("hankcs在臺灣寫程式碼")) # 简体转香港繁体 print(HanLP.s2hk("hankcs在香港写代码")) # 香港繁体转简体 print(HanLP.hk2s("hankcs在香港寫代碼")) # 香港繁体转台湾繁体 print(HanLP.hk2tw("hankcs在臺灣寫代碼")) # 台湾繁体转香港繁体 print(HanLP.tw2hk("hankcs在香港寫程式碼"))
17.
18.# 香港/台湾繁体和HanLP标准繁体的互转
19.print(HanLP.t2tw("hankcs在臺灣寫代碼"))
20.print(HanLP.t2hk("hankcs在臺灣寫代碼"))
21.
22.print(HanLP.tw2t("hankcs在臺灣寫程式碼"))
23.print(HanLP.hk2t("hankcs在台灣寫代碼"))


1.「以後等你當上皇后,就能買草莓慶祝了」。發現一根白頭髮
2.凭借笔记本电脑写程序HanLP
3.hankcs在臺灣寫程式碼
4.hankcs在台湾写代码
5.hankcs在香港寫代碼
6.hankcs在香港写代码
7.hankcs在臺灣寫程式碼
8.hankcs在香港寫代碼
9.hankcs在臺灣寫程式碼
10.hankcs在台灣寫代碼
11.hankcs在臺灣寫代碼
12.hankcs在臺灣寫代碼
---------------------
作者:Font Tian
人工智能
2019-03-29 10:14:00
「深度学习福利」大神带你进阶工程师,立即查看>>> 首先感谢 AlexeyAB 大神的 darknet 版本 下载编译就不说了简单说下训练。 首先在目录找到cfg/darknet19.cfg [net] # Training #batch=128 #subdivisions=2 # Testing batch=1 subdivisions=1 height=256 width=256 min_crop=128 max_crop=448 channels=3 momentum=0.9 decay=0.0005 burn_in=1000 learning_rate=0.1 policy=poly power=4 max_batches=800000 angle=7 hue=.1 saturation=.75 exposure=.75 aspect=.75 [convolutional] batch_normalize=1 filters=32 size=3 stride=1 pad=1 activation=leaky [maxpool] size=2 stride=2 [convolutional] batch_normalize=1 filters=64 size=3 stride=1 pad=1 activation=leaky [maxpool] size=2 stride=2 [convolutional] batch_normalize=1 filters=128 size=3 stride=1 pad=1 activation=leaky [convolutional] batch_normalize=1 filters=64 size=1 stride=1 pad=1 activation=leaky [convolutional] batch_normalize=1 filters=128 size=3 stride=1 pad=1 activation=leaky [maxpool] size=2 stride=2 [convolutional] batch_normalize=1 filters=256 size=3 stride=1 pad=1 activation=leaky [convolutional] batch_normalize=1 filters=128 size=1 stride=1 pad=1 activation=leaky [convolutional] batch_normalize=1 filters=256 size=3 stride=1 pad=1 activation=leaky [maxpool] size=2 stride=2 [convolutional] batch_normalize=1 filters=512 size=3 stride=1 pad=1 activation=leaky [convolutional] batch_normalize=1 filters=256 size=1 stride=1 pad=1 activation=leaky [convolutional] batch_normalize=1 filters=512 size=3 stride=1 pad=1 activation=leaky [convolutional] batch_normalize=1 filters=256 size=1 stride=1 pad=1 activation=leaky [convolutional] batch_normalize=1 filters=512 size=3 stride=1 pad=1 activation=leaky [maxpool] size=2 stride=2 [convolutional] batch_normalize=1 filters=1024 size=3 stride=1 pad=1 activation=leaky [convolutional] batch_normalize=1 filters=512 size=1 stride=1 pad=1 activation=leaky [convolutional] batch_normalize=1 filters=1024 size=3 stride=1 pad=1 activation=leaky [convolutional] batch_normalize=1 filters=512 size=1 stride=1 pad=1 activation=leaky [convolutional] batch_normalize=1 filters=1024 size=3 stride=1 pad=1 activation=leaky [convolutional] filters=1000 size=1 stride=1 pad=1 activation=linear [avgpool] [softmax] groups=1 样本就是 类别_随机数.png 也可以 随机数_类别.png 网上很多人博客说什么不要在路径出现多个或者无类别的单词,很多人照猫画虎不解其意实际上我们打开源码根据错误定位到这个函数 void fill_truth(char *path, char **labels, int k, float *truth) { int i; memset(truth, 0, k*sizeof(float)); int count = 0; for(i = 0; i < k; ++i){ if(strstr(path, labels[i])){ truth[i] = 1; ++count; //printf("%s %s %d\n", path, labels[i], i); } } if(count != 1 && (k != 1 || count != 0)) printf("Too many or too few labels: %d, %s\n", count, path); } 路径和类别strstr判断的 所以我们可以知道只要路径上只需要一个类别字符就可以了。甚至可以每个样本放在每个文件夹也是可以的 编译的时候会有些小问题,这个版本的darknet貌似为了rpc 加了一些c++的代码可以把那几个cpp删掉依赖也注释就行了实际上用不到的 搞定这个就可以愉快的训练了,有作者提供的darknet系列分类网络或者res系列的就可以训练分类器了。 具体配置网上一大堆,盘他就对了
人工智能
2019-03-28 15:10:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
阿里妹导读 :随着深度学习,尤其是CNN和RNN等技术的飞速发展,文字识别技术(OCR)近几年得到了迅速的提升。与此同时,在智能化终端的大趋势下,本地化智能识别凭借更高效快捷的体验以及高度的隐私保护和零流量消耗等优势备受瞩目和亲睐,越来越多的应用算法开始倾向终端化完成,OCR也不例外。接下来,蚂蚁金服的算法专家亦弦为我们剖析这个轻量而精准的移动端OCR引擎——xNN-OCR。
背景及概述
移动端OCR的优势
受算法效率和算法模型大小的限制和约束,目前大部分的OCR端上应用都是上传图片到服务端识别再将识别结果回传到客户端。虽然满足了部分业务需求,但一方面,对一些实效性要求较高的业务场景来说用户体验无疑是一个巨大的损失,尤其是弱网环境下。另一方面,在面临大促业务并发请求量过大的情况下,服务端不得不采用降级方案,而如果端上也具备识别能力的话,便可以极大地减少服务端的压力。此外,涉及到身份证、银行卡等重要私人证件采用OCR进行信息提取的时候,端上“识完即焚”这种方式,对这种敏感数据和隐私保护来说是天然的堡垒。因此,具备终端OCR识别能力有着极其重要的业务价值和意义。
移动端OCR的难点
OCR采用深度学习技术使得识别精度在特定的场景下面有了一定的保障,但模型大小和速度问题在端上依然是一大难题。目前大部分的后台OCR模型通常几十M或者上百M,可能比整个App安装包都要大,是不可能直接放到移动端的,而如果走实时下载的办法,模型过大也会造成下载失败率高、等待时间长、App占用空间大、流量消耗大等问题。另外,现在很多OCR算法在云端GPU上运行尚且需要几十到上百毫秒,要在手机CPU上保持较高的运行效率是一个极大的挑战。
我们做了什么?——xNN-OCR
xNN-OCR是专门针对移动端本地识别研发的的高精度、高效率、轻体量文字识别引擎,目前支持场景数字、场景英文、场景汉字以及特殊符号的识别。xNN-OCR针对移动端开发和优化了一套基于深度学习的文字检测和文字行识别算法框架,结合xNN的网络压缩和加速能力,检测和识别模型可以压缩到数百K级别,在中端及以上手机CPU上达到实时(最高15FPS),可结合“扫一扫”的模式在视频流中做到所见即所得。
移动端OCR识别技术
移动端OCR技术主要分为二个方面,一是OCR算法框架的研究和优化,主要目标是探索高精度且轻量级的检测和识别框架,确保在压缩之前模型的大小和速度在一个适当的范围以内,二是利用xNN对模型进行剪枝和量化压缩到实际应用需要的大小。下图是我们以银行卡检测和识别模型为例子展示整个压缩流程精度和模型的变化,其他OCR场景识别均是类似流程。
银行卡检测/识别模型压缩
轻量级OCR算法框架的探索
目前大部分的移动端OCR技术都是以传统算法为主,在复杂自然场景下识别率相对较低,而基于深度学习的方案可以很好的解决这一类问题,识别率和稳定性远超传统算法。目前主流的深度学习OCR主要分为文字行检测和行识别两大块,下面我们分别介绍下:
文字行检测
在检测方面,我们将物体检测的Region-CNN框架与FCN的图像分割框架融合在一起,保留了FCN的简单框架以适应端上对模型尺寸和预测时间的要求,同时又在模型中加入了目标检测的位置回归模块,实现了对任意形状文本的检测能力。在基于FCN的整体框架中,为了在精简模型的同时不降低检测效果,我们采用了各种模型精简结构(例如Separable Convolution、Group Convolution + Channel Shuffle等,如下图),模型的尺寸虽然不断减小,精度并未随之下降,在满足端上对模型的苛刻限制的同时取得了较好的检测效果。
Group Convolution + Channel Shuffle
文字行识别
在识别方面,我们在CRNN(CNN+LSTM+CTC)框架基础上进行了优化改进,在Densenet的基础上结合Multiscale Feature、Channel-wise Attention等技术设计出了一套专门用于移动端文字行识别的轻量级CNN网络,同时对LSTM内部参数采用Project技术、全连接层采用SVD、BTD等降维技术进一步减少参数数量(如下图),在ICDAR2013数据集(NOFINETUNE)上,模型大小下降约50%的前提下识别率高出CRNN近4个点,这一改进优化点为上端打下了强有力的基础。
LSTM Projection
xNN模型压缩
目前我们的OCR算法模型都是基于tensorflow开发的,xNN已经增加了对TFLite模型的支持,并且在性能上已经远超TFLite。xNN对于我们OCR算法的模型压缩比在10-20倍之间,不同的场景稍微有些区别,与此同时,压缩后模型的精度基本保持不变。由于OCR是一个较复杂的识别任务,算法模型通常都非常大,并且目前大部分的后台OCR算法都是在GPU上运行,要想在端上运行,除了需要在算法层次上做很多优化外,更需要xNN强大的模型压缩和加速能力。
移动端OCR应用
OCR技术是信息提取和场景理解极其重要的技术手段之一,应用领域非常广泛。目前移动端本地OCR应用从技术角度可以分为2大类,一类是印刷体文字识别,主要是针对字体变化不大、背景单一的场景,例如身份证识别、名片识别、车牌识别等等,另一类是场景类文字识别,主要是针对字体变化大且背景复杂的场景,例如银行卡识别、燃气表/水表识别、门头名识别、场景英文识别(AR翻译)等等,这两类场景中后者识别难度较大,面临的挑战性更多。我们将xNN-OCR用于这些场景并根据场景的特点做了各种优化,取得了一系列的成果,特别是在复杂环境下面识别依然可以保持高效和精准,具体的数据如下表。下面简介了几个比较重要和常见的应用场景。
OCR部分业务场景数据指标 银行卡识别:银行卡识别是金融类行业非常重要的一项技术,是场景数字类识别的一个典型代表。目前大部分银行卡识别均是采用端上识别的方案,因为端上识别不仅能带来更好更快的体验,同时由于不需要数据上传也能一定程度保护用户的隐私数据。基于xNN-OCR开发的银行卡识别在中端手机上耗时<300ms,大部分银行卡都是秒识别。此外,在面对复杂背景以及复杂环境干扰的时候,xNN-OCR在识别速度和精度上均展现了非常明显的优势。 燃气表识别:通过OCR识别燃气表读数是目前燃气自助抄表中的一项关键性技术,相比于传统上门抄表,一方面可以节省很大的人力物力,避免上门抄表带来的麻烦,另外一方面也可以减少漏抄、误抄等问题。目前已经有很多燃气公司已经开始应用这一项技术,但实际应用过程中,由于燃气表的位置有时候比较隐蔽,拍摄角度和光照难以控制,通常一般的用户拍照上传到后台识别的图片质量都比较差,识别率偏低。xNN-OCR在端上完成整套识别流程,通过识别反馈引导用户拍摄,可较大程度的提升识别率,在与一家燃气公司的合作中,我们测试识别率可以达到93%+,模型尺寸可保持在500k以内,识别成功耗时<1s。 车牌/VIN码识别:车牌/VIN码识别是传统印刷体类文字应用的一个经典场景,在移动警务、车辆维修定损等日常场景中起着非常重要的作用。由于车牌/VIN码识别在实际应用中可能同时需要,为了避免交互流程上的繁琐以及端上2套算法模型过大,xNN-OCR将车牌和VIN码这2个场景识别合二为一,模型尺寸依然<500k,在中端手机上识别成功耗时<1s,并且对光照、模糊、拍摄角度等干扰因素不敏感,同时由于端上可以反复识别寻求置信度最高的结果作为最终结果,所以相对于后台识别“一锤子买卖”而言,在识别精度上会更胜一筹。 身份证识别:身份证识别也是金融类行业非常重要的一项技术,在实名认证、安全审核等场景起着非常重要的作用,但由于中文汉字字库较大,导致模型较大,目前大部分的身份证识别均采用的是服务端识别,但由于端侧质量难以控制,往往会导致体验和精度上面难以均衡。xNN-OCR在大字库中文识别方面也作出了一些突破,整体模型小于1M,在端侧用单字识别信度控制识别精度,避免了对图片质量判断的依赖,通过多帧融合提升识别效率,单次识别中端手机上<600ms,识别成功<2s。
展望
xNN-OCR目前在端上已经能较好的识别场景数字、英文以及部分汉字,无论是模型大小、速度、准确度均已达到工业应用的水平,并且全面超过基于传统算法识别的OCR端上应用,在多个实际应用项目中对比得以验证。另外,我们在端上全量7000多类汉字识别上也做出了一些成果,在不久的将来会分享出来,欢迎有兴趣的同学来一起研究和探讨。
我们坚信,随着深度学习的移动端化逐步增强和移动硬件设备的逐步升级,终端智能化的应用与业务将会越来越多,未来xNN-OCR必将会给OCR相关的业务带来更深远的影响和更高的价值。
作者:亦弦
原文链接
​本文为云栖社区原创内容,未经允许不得转载。
人工智能
2019-04-03 11:03:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
结构化感知机标注框架是一套利用感知机做序列标注任务,并且应用到中文分词、词性标注与命名实体识别这三个问题的完整在线学习框架,该框架利用1个算法解决3个问题,时自治同意的系统,同时三个任务顺序渐进,构成流水线式的系统。本文先介绍中文分词框架部分内容。
中文分词
训练
只需指定输入语料的路径(单文档时为文件路径,多文档时为文件夹路径,灵活处理),以及模型保存位置即可:
命令行
java -cp hanlp.jar com.hankcs.hanlp.model.perceptron.Main -task CWS -train -reference data/test/pku98/199801.txt -model data/test/perceptron/cws.bin
API

public void testTrain() throws Exception
{
PerceptronTrainer trainer = new CWSTrainer();
PerceptronTrainer.Result result = trainer.train(
"data/test/pku98/199801.txt",
Config.CWS_MODEL_FILE
);
// System.out.printf("准确率F1:%.2f\n", result.prf[2]);
}
事实上,视语料与任务的不同,迭代数、压缩比和线程数都可以自由调整,以保证最佳结果:

/**
* 训练
*
* @param trainingFile 训练集
* @param developFile 开发集
* @param modelFile 模型保存路径
* @param compressRatio 压缩比
* @param maxIteration 最大迭代次数
* @param threadNum 线程数
* @return 一个包含模型和精度的结构
* @throws IOException
*/
public Result train(String trainingFile, String developFile,
String modelFile, final double compressRatio,
final int maxIteration, final int threadNum) throws IOException
单线程时使用AveragedPerceptron算法,收敛较好;多线程时使用StructuredPerceptron,波动较大。关于两种算法的精度比较,请参考下一小节。目前默认多线程,线程数为系统CPU核心数。请根据自己的需求平衡精度和速度。

准确率

在sighan2005的msr数据集上的性能评估结果如下:
语料未进行任何预处理 只使用了7种状态特征,未使用词典 压缩比0.0,迭代数50 总耗时包含语料加载与模型序列化 对任意PerceptronTagger,用户都可以调用准确率评估接口:


/**
* 性能测试
*
* @param corpora 数据集
* @return 默认返回accuracy,有些子类可能返回P,R,F1
* @throws IOException
*/
public double[] evaluate(String corpora) throws IOException
速度
目前感知机分词是所有“由字构词”的分词器实现中最快的,比自己写的CRF解码快1倍。新版CRF词法分析器框架复用了感知机的维特比解码算法,所以速度持平。
测试时需关闭词法分析器的自定义词典、词性标注和命名实体识别 测试环境 Java8 i7-6700K
测试
测试时只需提供分词模型的路径即可:

public void testCWS() throws Exception
{
PerceptronSegmenter segmenter = new PerceptronSegmenter(Config.CWS_MODEL_FILE);
System.out.println(segmenter.segment("商品和服务"));
}

正常情况下对商品和服务的分词结果为[商品, 和, 服务]。建议在任何语料上训练时都试一试这个简单的句子,当作HelloWorld来测试。若这个例子都错了,则说明语料格式、规模或API调用上存在问题,须仔细排查,而不要急着部署上线。

另外,数据包中已经打包了在人民日报语料1998年1月份上训练的模型,不传路径时将默认加载配置文件中指定的模型。

在本系统中,分词器PerceptronSegmenter的职能更加单一,仅仅负责分词,不再负责词性标注或命名实体识别。这是一次接口设计上的新尝试,未来可能在v2.0中大规模采用这种思路去重构。
人工智能
2019-04-03 10:23:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
《用Python中的自定义损失函数和正则化来拟合线性模型》
假设有100个样本点,每个样本点的feature是10维(9个基础变量和1个截距),为了更好地展现实验效果,我们为样本添加噪声:
# Generate predictors X_raw = np.random.random(100*9) X_raw = np.reshape(X_raw, (100, 9)) # Standardize the predictors scaler = StandardScaler().fit(X_raw) X = scaler.transform(X_raw) # Add an intercept column to the model. X = np.abs(np.concatenate((np.ones((X.shape[0],1)), X), axis=1)) # Define my "true" beta coefficients beta = np.array([2,6,7,3,5,7,1,2,2,8]) # Y = Xb Y_true = np.matmul(X,beta) # Observed data with noise Y = Y_true*np.exp(np.random.normal(loc=0.0, scale=0.2, size=100))
其中有2种选择:
Mean Absolute Percentage Error (MAPE)
Weighted MAPE
损失函数为: def mean_absolute_percentage_error(y_true, y_pred, sample_weights=None): y_true = np.array(y_true) y_pred = np.array(y_pred) assert len(y_true) == len(y_pred) if np.any(y_true==0): print("Found zeroes in y_true. MAPE undefined. Removing from set...") idx = np.where(y_true==0) y_true = np.delete(y_true, idx) y_pred = np.delete(y_pred, idx) if type(sample_weights) != type(None): sample_weights = np.array(sample_weights) sample_weights = np.delete(sample_weights, idx) if type(sample_weights) == type(None): return(np.mean(np.abs((y_true - y_pred) / y_true)) * 100) else: sample_weights = np.array(sample_weights) assert len(sample_weights) == len(y_true) return(100/sum(sample_weights)*np.dot( sample_weights, (np.abs((y_true - y_pred) / y_true)) ))
传统求解方式
本方法的求解方式:
相应的代码为: from scipy.optimize import minimize loss_function = mean_absolute_percentage_error def objective_function(beta, X, Y): error = loss_function(np.matmul(X,beta), Y) return(error) # You must provide a starting point at which to initialize # the parameter search space beta_init = np.array([1]*X.shape[1]) result = minimize(objective_function, beta_init, args=(X,Y), method='BFGS', options={'maxiter': 500}) # The optimal values for the input parameters are stored # in result.x beta_hat = result.x print(beta_hat)
人工智能
2019-04-02 16:11:06
「深度学习福利」大神带你进阶工程师,立即查看>>>
关于智能对话分析服务
智能对话分析服务 (Smart Conversation Analysis) 依托于阿里云语音识别和自然语言分析技术,为企业用户提供智能的对话分析服务,支持语音和文本数据的接入。可用于电话/在线客服坐席服务质量检测、风险监控识别、服务策略优化等场景。
智能对话分析服务架构图
产品优势 智能规则:按照需求自由定制,通过规则间的逻辑组合深入挖掘数据信息。 全量自动化:告别传统抽样带来的分析盲区,视野广阔,一览无余! 灵活接入:支持对象存储OSS数据源无缝接入,省时省力。 复核校验:人机配合的方式对分析结果做二次校验,提升结果准确性。
名词解释 算子:对检查范围限定的句字逐句做出判定。如问句检测、意思重复、字数相近、语义匹配、正则表达式等待。 条件:条件由检查范围和算子组成,如「客服的第一句话是 “你好”」这个条件的检查范围是「客服说的第一句话」,算子是「出现关键字 “你好”」,一个条件内可以包含多个算子组成的逻辑表达式。 规则:规则是由逻辑运算符(&&, ||, !, !=, ==)和条件组成的表达式。 命中:如果某对话内容符合规则中定义的条件,称为被这个规则命中。 语义匹配:从相似性的角度找到语义相似的语句,比如「你的ip是?」和「你的ip地址是多少?」这两句话在语义上是高度相似的。
典型应用场景 服务质量检测:服务质量是企业对外的形象窗口,企业客户服务人员的行为是否符合规范?服务质量是否达标?基于智能对话分析服务提供的对电话/在线客服坐席的对话分析结果,企业可以在服务意识、服务技巧等方面优化培训方案,增强客服人员的服务能力。 风险监控识别:互联网时代口碑传播的速度越来越快,行业和企业每天都面临着新的问题和挑战,智能对话分析服务的语义分析技术能实现对潜在风险监控和识别,使企业有效应对未来的不确定性,变风险为机会,使得企业价值最大化。 服务策略优化:用户数据中常常隐含着各类用户的第一手反馈、意见、满意度等重要信息,将这些信息以统计量化的形式表达,能够提供企业在服务转型、升级、发展中重要的参考资料,使企业在新的竞争中保持信息上的优势。
关于阿里云智能对话分析服务更多内容: 阿里云智能对话分析服务使用教程
(智能对话分析服务 (Smart Conversation Analysis) 能实现从对话录音或者对话文本中,基于智能规则,分析对话内容,挖掘对话中可能存在的问题和机会。能帮助企业提升服务质量、监控舆情风险、优化服务策略,典型应用场景有智能客服质检、销售机会分析等。)
更多精品课程:
阿里云大学官网 —云生态下的创新人才工场
人工智能
2019-04-02 15:49:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
由于项目需要在Android手机设备上实现汉字转拼音功能(支持多音字),于是首先想到了Pinyin4j+多音字映射对照表的实现方案,并在项目中试用了一段时间,发现数据量大时,其耗时非常严重。后来寻找其他方案,在github上找到了HanLP开源库,其多音字转换速度非常快,但是没有针对Android平台进行适配,于是对代码进行了一些修改,终于可以在Android手机上运行。修改后的工程已上传至github,如有需要,可以clone HanLP-Android代码后,导出jar文件使用。

##使用说明: ###1、将生成的jar文件拷贝到app/libs目录下,将HanLP-Android工程下dictionary目录拷贝至Android项目app module的assets目录下
---------------------
人工智能
2019-04-01 13:39:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
今天( 3 月 19日),NVIDIA GPU 技术大会在美国加利福尼亚州圣何塞市开幕。会上,NVIDIA 宣布推出 一款全新的人工智能计算机Jetson Nano,可以创建数百万个智能系统。
  这款CUDA-X人工智能计算机外观小巧但功能强大,可提供472 GFLOPS(每秒十亿次浮点运算)的计算性能以支持现代人工智能工作负载运行,而耗电量仅为 5 瓦。
  Jetson Nano 支持高分辨率传感器,可以并行处理多个传感器,并且可在每个传感器流上运行多个现代神经网络。它还支持许多常见的人工智能框架,让开发人员轻松地将自己偏爱的模型及框架集成到产品中。
  NVIDIA 创始人兼CEO黄仁勋在大会上公布了两个版本的 Jetson Nano:1、面向开发人员、创客和技术爱好者的,售价为 99 美元的开发者套件;2、为希望面向大众市场创建边缘系统的企业提供售价为 129 美元的生产就绪型模块。
  Jetson Nano属于Jetson产品系列,该系列还包括用于完全自主机器的Jetson AGX Xavier和用于边缘人工智能的 Jetson TX2。作为大型企业、初创公司和研究人员的理想选择,Jetson 平台通过 Jetson Nano 将其适用范围扩展到全球 3000 万名创客、开发人员、发明者和学生。
  NVIDIA 副总裁兼自主机器事业部总经理 Deepu Talla 表示:“Jetson Nano让所有人都能更容易地使用人工智能,并且由世界领先超级计算机所采用的基础架构和软件提供的支持。将人工智能带入创客运动,开启了一个全新的创新世界,激励人们创造出下一个重大产品。”
  面向开发人员、创客:Jetson Nano 开发者套件
  人工智能在创客社区和教育领域远未发挥出最大价值,原因在于一般的技术往往计算能力不足并且缺乏人工智能软件平台。
  售价 99美元的 Jetson Nano开发者套件将现代人工智能的力量引入到一个低成本的平台,以支持创客、发明者、开发人员和学生的新一轮创新浪潮。他们可以构建以往无法实现的人工智能项目,并将现有项目提升到一个新水平——包括移动机器人和无人机、数字助理、自动化设备等。
  该套件可为全桌面 Linux 给予开箱即用支持,与许多常见的外部设备和配件兼容,提供随 时可用的项目和教程,以帮助创客快速上手人工智能。此外,NVIDIA Jetson开发者论坛也 能够帮助人们获得各种技术问题的答案。
  DIY Robocars、DIY无人机和Linux基金会Dronecode项目的Chris Anderson表示:“Jetson Nano 开发者套件令人眼前一亮,因为它以一种非常易用的方式将先进的人工智能引入 DIY运动。我们计划将这项技术带入我们的创客社区,因为这是一个功能强大、趣味无限且价格合理的平台,是向更广泛的受众传授深度学习和机器人技术的好方式。”
  面向企业:Jetson Nano 模块
  过去,企业一直受到尺寸、功耗、成本和人工智能计算密度等挑战的制约。Jetson Nano模块为网络录像机、家用机器人和具有完整分析功能的智能网关等嵌入式应用开启了新世界。它旨在为复杂、稳健、节能的人工智能系统的硬件设计、测试和验证节约时间,从而缩短总体开发时间,更快地将产品推向市场。
  该设计配有电源管理、时钟、内存和完全可访问的输入/输出。因为人工智能工作负载完 全由软件定义,所以即使在系统部署之后,企业也可以更新性能和功能。
  思科 Webex 设备副总裁兼总经理 Sandeep Mehra 表示:“思科协作会议(Cisco Collaboration)的使命在于将所有人、所有地点都联结起来,以实现身临其境的沉浸式会议。与 NVIDIA 的合作以及对 Jetson 系列产品的使用是我们取得成功的关键。得益于Jetson 平台先进的边缘人工智能处理能力,我们推动了新的体验,帮助人们更好地开展工作。”
  为了协助客户轻松地将人工智能和机器学习工作负载转移到边缘,NVIDIA 与亚马逊 AWS 展开了合作,以支持 AWS IoT Greengrass 能够完美运行 Jetson Nano 等由 Jetson 驱动的设备。
  AWS 物联网副总裁 Dirk Didascalou 表示:“我们的客户遍及各行各业,包括能源管理、工业、物流、智能建筑和住宅。所有这些行业的参与者都正在将智能和计算机视觉应用到各自的应用程序中,从而以近乎实时的方式在边缘执行操作。AWS IoT Greengrass 可以让我 们的客户在Jetson驱动的设备上执行本地推理,并将相关数据返回至云端,以改进模型训练。”
  NVIDIA CUDA-X 包含 40 多个加速库,以支持现代计算应用程序从 NVIDIA GPU 加速计算平 台中获益。JetPack SDK建立在 CUDA-X 的基础上,是一个完整的人工智能软件栈,包含 用于深度学习、计算机视觉、计算机图形和多媒体处理的加速库,支持整个Jetson系列品。
  JetPack 包括最新版本的 CUDA、cuDNN、TensorRT™和完整版桌面 Linux 操作系统。 Jetson 与 NVIDIA 人工智能平台相兼容,这是 NVIDIA 为推进人工智能计算科学而进行的一 个长达十年之久、耗费数十亿美元的巨大投资。
  NVIDIA 还创建了一个参考平台,通过尽量减少初始硬件组装所用的时间来快速启动人工智 能应用的开发。NVIDIA JetBot是一款小型移动机器人,可使用现成的组件构建并基于GitHub 实现开源。
  Jetson Nano 系统规格和软件
  Jetson Nano 的主要特性包括:
  • GPU:128 核基于 NVIDIA Maxwell架构的 GPU
  • CPU:四核 ARM®A57
  • 视频:4K @30 fps(H.264/H.265)/4K @60 fps(H.264/H.265)编码和解码
  • 摄像头:MIPI CSI-2 DPHY 通道,12 个模块和 1 个开发工具包
  • 内存:4 GB 64 位 LPDDR4;25.6 GB/秒
  • 连接性:千兆以太网
  • 操作系统支持:面向 Tegra®的 Linux
  • 模块尺寸:70mm x 45mm
  • 开发者套件尺寸:100mm x 80mm
  NVIDIA Jetson Nano 开发者套件现已正式上市,售价为 99 美元。Jetson Nano 模块售价为129 美元(1000 个起售),将于 6 月开始发货。两个产品都将通过 NVIDIA 的主要全球经 销商销售
人工智能
2019-04-01 13:21:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
因为项目内容中涉及自动生成文本摘要的功能,因此学习了一下TextRank算法实现摘要提取。
1.介绍一下TextRank算法
TextRank算法的思想是,拟定一个通用的评分标准,给文本中的每一个句子打分,所得分数就是该句子的权重,最后得到权重排名靠前的几个句子,构成最终的文本摘要。这就是所谓的TextRank自动生成摘要。
TextRank对每一个句子的打分思想由PageRank的迭代思想衍生而来,公式如下:
在这个公式中,WS(i)表示第i个句子的权重,右侧的求和表示的是每一个句子对所在文本的贡献程度。求和部分的分子wji表示两个句子j和i的相似程度,分母则是文本中相对应的部分的句子的权重之和,WS(Vj)表示上次迭代j的权重。
从整个公式可以看出这是一个反复迭代的过程。
由于wji需要计算两个句子的相似程度,因此这里我们需要引入另外的相似度算法,通常推荐使用BM25,这是目前已知的计算结果较为准确的算法之一。
这个公式中的d是一个常数,称为阻尼系数,通常在算法中我们取其值为0.85,当然也可以根据实际的情况进行调整。
2.TextRank算法的Java实现
2.1首先需要声明相关的变量
/**
* 阻尼系数(DampingFactor),一般取值为0.85
*/
final double d = 0.85f;
/**
* 最大迭代次数
*/
final int max_iter = 200;
final double min_diff = 0.001f;
/**
* 文档句子的个数
*/
int D;
/**
* 拆分为[句子[单词]]形式的文档
*/
List> docs;
/**
* 排序后的最终结果 score <-> index
*/
TreeMap top;
/**
* 句子和其他句子的相关程度
*/
double[][] weight;
/**
* 该句子和其他句子相关程度之和
*/
double[] weight_sum;
/**
* 迭代之后收敛的权重
*/
double[] vertex;
/**
* BM25相似度
*/
BM25 bm25;
2.2 实现文本切割成句子
List sentences = new ArrayList();
if (document == null) return sentences;
for (String line : document.split("[\r\n]"))
{
line = line.trim();//去掉字符串的首尾空格
if (line.length() == 0) continue;
for (String sent : line.split("[,,.。::“”??!!;;]"))
{
sent = sent.trim();
if (sent.length() == 0) continue;
sentences.add(sent);
}
}
2.3 使用开源jar包对中文文本进行分词,并获得分词结果
List sentenceList = spiltSentence(document);//用,。”“等标点将文本切割成了短句
List> docs = new ArrayList>();
for (String sentence : sentenceList)
{
List termList = ToAnalysis.parse(sentence);//使用开源的ansj对句子进行分词
List wordList = new LinkedList();
for (Term term : termList)
{
if (shouldInclude(term))
{
wordList.add(term.getRealName());
}
}
docs.add(wordList);
}
TextRankSummary textRankSummary = new TextRankSummary(docs);
int[] topSentence = textRankSummary.getTopSentence(size);
List resultList = new LinkedList();
for (int i : topSentence)
{
resultList.add(sentenceList.get(i));
}
return resultList;
2.4 运用公式计算出每个句子的权重并进行排序
int cnt = 0;
for (List sentence : docs)
{
double[] scores = bm25.simAll(sentence);//计算相似度
System.out.println(Arrays.toString(scores));
weight[cnt] = scores;
weight_sum[cnt] = sum(scores) - scores[cnt]; // 减掉自己,自己跟自己肯定最相似
vertex[cnt] = 1.0;
++cnt;
}
for (int _ = 0; _ < max_iter; ++_)
{
double[] m = new double[D];
double max_diff = 0;
for (int i = 0; i < D; ++i)
{
m[i] = 1 - d;
for (int j = 0; j < D; ++j)
{
if (j == i || weight_sum[j] == 0) continue;
m[i] += (d * weight[j][i] / weight_sum[j] * vertex[j]);
}
double diff = Math.abs(m[i] - vertex[i]);
if (diff > max_diff)
{
max_diff = diff;
}
}
vertex = m;
if (max_diff <= min_diff) break;
}
//
for (int i = 0; i < D; ++i)
{
top.put(vertex[i], i);
}
2.5 设置想要获取的目标摘要长度,并根据排序结果取出摘要
Collection values = top.values();
size = Math.min(size, values.size());
int[] indexArray = new int[size];
Iterator it = values.iterator();
for (int i = 0; i < size; ++i)
{
indexArray[i] = it.next();
}
return indexArray;
以上就是对TextRank算法的学习内容的概括。
---------------------
作者:GY_Grace
来源:CSDN
原文:https://blog.csdn.net/GY_Grace/article/details/72863632
版权声明:本文为博主原创文章,转载请附上博文链接!
人工智能
2019-04-01 00:00:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
自动文本摘要通常可分为两类,分别是抽取式(extractive)和生成式(abstractive)。
(1)抽取式摘要判断原文本中重要的句子,抽取这些句子成为一篇摘要。
(2)生成式方法则应用先进的自然语言处理的算法,通过转述、同义替换、句子缩写等技术,生成更凝练简洁的摘要。比起抽取式,生成式更接近人进行摘要的过程。历史上,抽取式的效果通常优于生成式。伴随深度神经网络的兴起和研究,基于神经网络的生成式文本摘要得到快速发展,并取得了不错的成绩。
https://www.sohu.com/a/160168357_642762
http://www.360doc.com/content/17/0831/04/37113458_683452166.shtml
https://www.jianshu.com/p/abc7e13abc21
---------------------
作者:小妖精Fsky
来源:CSDN
原文:https://blog.csdn.net/appleml/article/details/81941776
版权声明:本文为博主原创文章,转载请附上博文链接!
人工智能
2019-03-31 23:59:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
上次写过《 TextRank算法提取关键词的Java实现 》,这次用TextRank实现文章的自动摘要。
所谓自动摘要,就是从文章中自动抽取关键句。何谓关键句?人类的理解是能够概括文章中心的句子,机器的理解只能模拟人类的理解,即拟定一个权重的评分标准,给每个句子打分,之后给出排名靠前的几个句子。
TextRank公式
TextRank的打分思想依然是从PageRank的迭代思想衍生过来的,如下公式所示:
等式左边表示一个句子的权重(WS是weight_sum的缩写),右侧的求和表示每个相邻句子对本句子的贡献程度。与提取关键字的时候不同,一般认为全部句子都是相邻的,不再提取窗口。
求和的分母wji表示两个句子的相似程度,分母又是一个weight_sum,而WS(Vj)代表上次迭代j的权重。整个公式是一个迭代的过程。
相似程度的计算
而相似程度wji的计算,推荐使用BM25 BM25算法,通常用来作搜索相关性平分。一句话概况其主要思想:对Query进行语素解析,生成语素qi;然后,对于每个搜索结果D,计算每个语素qi与D的相关性得分,最后,将qi相对于D的相关性得分进行加权求和,从而得到Query与D的相关性得分。
BM25算法.pdf
测试用例 算法可大致分为基本算法、数据结构的算法、数论算法、计算几何的算法、图的算法、动态规划以及数值分析、加密算法、排序算法、检索算法、随机化算法、并行算法、厄米变形模型、随机森林算法。
算法可以宽泛的分为三类,
一,有限的确定性算法,这类算法在有限的一段时间内终止。他们可能要花很长时间来执行指定的任务,但仍将在一定的时间内终止。这类算法得出的结果常取决于输入值。
二,有限的非确定算法,这类算法在有限的时间内终止。然而,对于一个(或一些)给定的数值,算法的结果并不是唯一的或确定的。
三,无限的算法,是那些由于没有定义终止定义条件,或定义的条件无法由输入的数据满足而不终止运行的算法。通常,无限算法的产生是由于未能确定的定义终止条件。

断句 算法可大致分为基本算法、数据结构的算法、数论算法、计算几何的算法、图的算法、动态规划以及数值分析、加密算法、排序算法、检索算法、随机化算法、并行算法、厄米变形模型、随机森林算法 算法可以宽泛的分为三类 一 有限的确定性算法 这类算法在有限的一段时间内终止 他们可能要花很长时间来执行指定的任务 但仍将在一定的时间内终止 这类算法得出的结果常取决于输入值 二 有限的非确定算法 这类算法在有限的时间内终止 然而 对于一个(或一些)给定的数值 算法的结果并不是唯一的或确定的 三 无限的算法 是那些由于没有定义终止定义条件 或定义的条件无法由输入的数据满足而不终止运行的算法 通常 无限算法的产生是由于未能确定的定义终止条件
分词并过滤停用词 [算法, 大致, 分, 基本, 算法, 数据, 结构, 算法, 数论, 算法, 计算, 几何, 算法, 图, 算法, 动态, 规划, 数值, 分析, 加密, 算法, 排序, 算法, 检索, 算法, 随机, 化, 算法, 并行, 算法, 厄, 米, 变形, 模型, 随机, 森林, 算法] [算法, 宽泛, 分为, 三类] [] [有限, 确定性, 算法] [类, 算法, 有限, 一段, 时间, 终止] [可能, 花, 长, 时间, 执行, 指定, 任务] [一定, 时间, 终止] [类, 算法, 得出, 常, 取决, 输入, 值] [二] [有限, 非, 确定, 算法] [类, 算法, 有限, 时间, 终止] [] [一个, 定, 数值] [算法, 唯一, 确定] [三] [无限, 算法] [没有, 定义, 终止, 定义, 条件] [定义, 条件, 无法, 输入, 数据, 满足, 终止, 运行, 算法] [通常] [无限, 算法, 产生, 未, 确定, 定义, 终止, 条件]
计算BM25相关性矩阵 [15.176530737482341, -2.604484103028904, 0.0, -2.8740684265166565, -2.1930693258940175, 0.0, 0.0, -2.0325355810136103, 0.0, -2.604484103028904, -2.3811362523642052, 0.0, 2.509043358515279, -2.8740684265166565, 0.0, -3.2059044218809922, 0.0, -0.22517864251663589, 0.0, -1.8939010965185548] [-0.2864022115473306, 8.52437122545896, 0.0, -0.23950570220972142, -0.18275577715783484, 0.0, 0.0, -0.1693779650844675, 0.0, -0.21704034191907534, -0.19842802103035043, 0.0, 0.0, -0.23950570220972142, 0.0, -0.267158701823416, 0.0, -0.1477475757866994, 0.0, -0.15782509137654627] [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] [-0.2864022115473306, -0.21704034191907534, 0.0, 4.604672851367114, 1.060086217315166, 0.0, 0.0, -0.1693779650844675, 0.0, 1.2589559610532894, 1.1509940396671094, 0.0, 0.0, -0.23950570220972142, 0.0, -0.267158701823416, 0.0, -0.1477475757866994, 0.0, -0.15782509137654627] [-0.2864022115473306, -0.21704034191907534, 0.0, 1.3892676764009562, 7.063472116341414, 1.1518653539666401, 2.634590118176154, 1.2574519044179069, 0.0, 1.2589559610532894, 5.005270773642655, 0.0, 0.0, -0.23950570220972142, 0.0, -0.267158701823416, 0.8333088661764476, 0.4727261064071153, 0.0, 0.504969645305668] [0.0, 0.0, 0.0, 0.0, 1.2428419944730007, 14.795434933306574, 1.6287733786106775, 0.0, 0.0, 0.0, 1.3494220606974598, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] [0.0, 0.0, 0.0, 0.0, 2.010334451736872, 1.1518653539666401, 5.849995293142312, 0.0, 0.0, 0.0, 2.1827309268739077, 0.0, 0.0, 0.0, 0.0, 0.0, 0.8333088661764476, 0.6204736821938147, 0.0, 0.6627947366822143] [-0.2864022115473306, -0.21704034191907534, 0.0, -0.23950570220972142, 1.356767982871274, 0.0, 0.0, 12.127555522767913, 0.0, -0.21704034191907534, 1.4731177860712878, 0.0, 0.0, -0.23950570220972142, 0.0, -0.267158701823416, 0.0, 1.4000446911370572, 0.0, -0.15782509137654627] [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.054814792796337, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] [-0.2864022115473306, -0.21704034191907534, 0.0, 1.3892676764009562, 1.060086217315166, 0.0, 0.0, -0.1693779650844675, 0.0, 6.001094704342757, 1.1509940396671094, 0.0, 0.0, 1.7780760396570634, 0.0, -0.267158701823416, 0.0, -0.1477475757866994, 0.0, 1.1716840594784514] [-0.2864022115473306, -0.21704034191907534, 0.0, 1.3892676764009562, 4.609944429081147, 1.1518653539666401, 2.634590118176154, 1.2574519044179069, 0.0, 1.2589559610532894, 5.005270773642655, 0.0, 0.0, -0.23950570220972142, 0.0, -0.267158701823416, 0.8333088661764476, 0.4727261064071153, 0.0, 0.504969645305668] [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] [0.5551884973225691, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 8.939853708447595, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] [-0.2864022115473306, -0.21704034191907534, 0.0, -0.23950570220972142, -0.18275577715783484, 0.0, 0.0, -0.1693779650844675, 0.0, 1.611294545577714, -0.19842802103035043, 0.0, 0.0, 4.9934812146232215, 0.0, -0.267158701823416, 0.0, -0.1477475757866994, 0.0, 1.1716840594784514] [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.054814792796337, 0.0, 0.0, 0.0, 0.0, 0.0] [-0.2864022115473306, -0.21704034191907534, 0.0, -0.23950570220972142, -0.18275577715783484, 0.0, 0.0, -0.1693779650844675, 0.0, -0.21704034191907534, -0.19842802103035043, 0.0, 0.0, -0.23950570220972142, 0.0, 2.531575358765468, 0.0, -0.1477475757866994, 0.0, 1.4955384555950606] [0.0, 0.0, 0.0, 0.0, 0.7674924572638717, 0.0, 1.0058167395654765, 0.0, 0.0, 0.0, 0.8333088661764476, 0.0, 0.0, 0.0, 0.0, 0.0, 9.892547495751218, 4.354323965031352, 0.0, 4.651322189247207] [0.26878628577523855, -0.21704034191907534, 0.0, -0.23950570220972142, 0.5847366801060369, 0.0, 1.0058167395654765, 1.6050126003722507, 0.0, -0.21704034191907534, 0.6348808451460972, 0.0, 0.0, -0.23950570220972142, 0.0, -0.267158701823416, 4.866735958438866, 12.008153881124132, 0.0, 3.1639879470156633] [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.054814792796337, 0.0] [-0.2864022115473306, -0.21704034191907534, 0.0, -0.23950570220972142, 0.5847366801060369, 0.0, 1.0058167395654765, -0.1693779650844675, 0.0, 1.611294545577714, 0.6348808451460972, 0.0, 0.0, 1.7780760396570634, 0.0, 2.531575358765468, 4.866735958438866, 2.9619596282988065, 0.0, 10.38451854500608]
迭代投票 for (int _ = 0; _ < max_iter; ++_) { double[] m = new double[D]; double max_diff = 0; for (int i = 0; i < D; ++i) { m[i] = 1 - d; for (int j = 0; j < D; ++j) { if (j == i || weight_sum[j] == 0) continue; m[i] += (d * weight[j][i] / weight_sum[j] * vertex[j]); } double diff = Math.abs(m[i] - vertex[i]); if (diff > max_diff) { max_diff = diff; } } vertex = m; if (max_diff <= min_diff) break; }
排序输出结果 这类算法在有限的时间内终止 这类算法在有限的一段时间内终止 无限算法的产生是由于未能确定的定义终止条件
分别对应于原文 算法可大致分为基本算法、数据结构的算法、数论算法、计算几何的算法、图的算法、动态规划以及数值分析、加密算法、排序算法、检索算法、随机化算法、并行算法、厄米变形模型、随机森林算法。
算法可以宽泛的分为三类,
一,有限的确定性算法,这类算法在有限的一段时间内终止。他们可能要花很长时间来执行指定的任务,但仍将在一定的时间内终止。这类算法得出的结果常取决于输入值。
二,有限的非确定算法,这类算法在有限的时间内终止。然而,对于一个(或一些)给定的数值,算法的结果并不是唯一的或确定的。
三,无限的算法,是那些由于没有定义终止定义条件,或定义的条件无法由输入的数据满足而不终止运行的算法。通常,无限算法的产生是由于未能确定的定义终止条件。
效果还行,对于三种算法的介绍,分别提取出了一句话概括语句,就我个人感觉来讲,如果我来人工选关键句,大概也就这三句了。
开源项目地址
本文代码已集成到HanLP中开源: http://www.hankcs.com/nlp/hanlp.html
目前能够提供分词、词性标注、命名实体识别、关键字提取、短语提取、自动摘要、自动推荐,依存关系、句法树等功能。
人工智能
2019-03-31 23:59:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
正交分解
矩阵的正交分解又称为QR分解,是将矩阵分解为一个正交矩阵Q和一个上三角矩阵的乘积的形式。
任意实数方阵A,都能被分解为 。这里的Q为正交单位阵,即 R是一个上三角矩阵。这种分解被称为QR分解。 QR分解也有若干种算法,常见的包括Gram–Schmidt、Householder和Givens算法。 QR分解是将矩阵分解为一个正交矩阵与上三角矩阵的乘积。用一张图可以形象地表示QR分解:
为啥我们需要正交分解呢?
实际运用过程中,QR分解经常被用来解线性最小二乘问题,这个问题我们后面讲述。
提到正交分解就不得不讨论(Householder transformation Householder变换)豪斯霍尔德变换和(Schmidt orthogonalization Schmidt正交化)施密特正交化
Schmidt正交化 定理1 设A是n阶实非奇异矩阵,则存在正交矩阵Q和实非奇异上三角矩阵R使A有QR分解;且除去相差一个对角元素的绝对值(模)全等于1的对角矩阵因子外,分解是唯一的. 定理2 设A是m×n实矩阵,且其n个列向量线性无关,则A有分解A=QR,其中Q是m×n实矩阵,且满足QHTQ=E,R是n阶实非奇异上三角矩阵该分解除去相差一个对角元素的绝对值(模)全等于1的对角矩阵因子外是唯一的.用Schmidt正交化分解方法对矩阵进行QR分解时,所论矩阵必须是列满秩矩阵。
算法步骤 写出矩阵的列向量; 列向量按照Schmidt正交化正交; 得出矩阵的Q′,R′; 对R′的列向量单位化得到Q,R′的每行乘R′每列的模得푹
matlab代码 function[X,Q,R] = QRSchmidt(A,b) %方阵的QR的Gram-Schmidt正交化分解法,并用于求解AX=b方程组[m,n]=size(A); if m~=n %如果不是方阵,则不满足QR分解要求 disp('不满足QR分解要求'); end Q=zeros(m,n); X=zeros(n,1); R=zeros(n); for k=1:nR(k,k)=norm(A(:,k)); if R(k,k)==0 break; end Q(:,k)=A(:,k)/R(k,k); for j=k+1:n R(k,j)=Q(:,k)'*A(:,j); A(:,j)=A(:,j)-R(k,j)*Q(:,k); end if nargin==2 b=Q'* b; X(n)=b(n)/R(n,n); for i=n-1:-1:1 X(i)=(b(i)-sum(R(i,i+1:n).*X(i+1:n)'))/R(i,i); end else X=[]; end end
Householder变换 设A为任一n阶方阵,则必存在n阶酉矩阵Q和n阶上三角阵R,使得A=QR
设w∈Cn是一个单位向量,令
则称H是一个Householder矩阵或Householder变换。则对于任意的 存在Householder矩阵H,使得Hx=-au。其中 酉矩阵(unitary matrix)
若n阶复矩阵A满足
则称A为酉矩阵,记之为 其中,Ah是A的共轭转置
酉矩阵性质
如果A是酉矩阵 也是酉矩阵; det(A)=1; 充分条件是它的n个列向量是两两正交的单位向量。
算法步骤 将矩阵A按列分块写成A=(α1,α2,...,αn).如果α1≠0,则可得,存在n阶householder矩阵H1使得 于是有
如果α1=0,则直接进行下一步,此时相当于取 ,而a1=0. 将矩阵An-1按列分块写成An-1=(αi,α2,... ,αn-1)。如果α1≠0,则可得,存在n-1阶householder矩阵H’2使得 于是有
此时,令
则H2是n阶Householder矩阵,且使
如果α1=0,则直接进行下一步 对n-2阶矩阵继续进行类似的变换,如此下去,之多在第n-1步,我们可以找到Householder矩阵H1,H2,...,Hn-1使得
令 ,则Q是酉矩阵之积,从而必有酉矩阵并且A=QR
matlab代码 function[ X,Q,R ] = QRHouseholder(A,b) %用Householder变换将方阵A分解为正交Q与上三角矩阵R的乘积,并用于求解AX=b方程组 [n,n]=size(A); E=eye(n); X=zeros(n,1); R=zeros(n); P1=E; for k=1:n-1 %构造w,使Pk=I-2ww' s=-sign(A(k,k))* norm(A(k:n,k)); R(k,k)=-s; if k==1 w=[A(1,1)+s,A(2:n,k)']'; else w=[zeros(1,k-1),A(k,k)+s,A(k+1:n,k)']'; R(1:k-1,k)=A(1:k-1,k); end if norm(w)~=0 w=w/norm(w); end P=E-2*w*w'; A=P*A; P1=P*P1; R(1:n,n)=A(1:n,n); end Q=P1'; if nargin==2 b=P1*b; X(n)=b(n)/R(n,n); for i=n-1:-1:1 X(i)=(b(i)-sum(R(i,i+1:n).*X(i+1:n)'))/R(i,i); end else X=[]; end
matlab自带方法 %产生一个3*3大小的魔方矩阵 A=magic(3) [Q,R]=qr(A)
使用Eigen C++ Eigen提供了几种矩阵分解的方法
分解方式 Method 矩阵满足条件 计算速度 计算精度 PartialPivLU partialPivLu() Invertible ++ +
FullPivLU fullPivLu() None - +++
HouseholderQR householderQr() None ++ +
ColPivHouseholderQR
FullPivHouseholderQR LLT LDLT
colPivHouseholderQr()
fullPivHouseholderQr() llt() ldlt()
None
None Positive definite Positive or negative semidefinite
+
- +++ +++
++
+++ + ++
其中HouseholderQR、ColPivHouseholderQR、FullPivHouseholderQR是我们目前要用到的QR分解方法
C++的QR分解代码为 #include #include using namespace Eigen; using namespace std; int main() { Matrix3d A; A<<1,1,1, 2,-1,-1, 2,-4,5; HouseholderQR qr; qr.compute(A); MatrixXd R = qr.matrixQR().triangularView(); MatrixXd Q = qr.householderQ(); std::cout << "QR2(): HouseholderQR---------------------------------------------"<< std::endl; std::cout << "A "<< std::endl <输出
好了大功告成,为什么我要写计算方法的文章呢,虽然现在有很多的库和包给我们调用,但是我们也不能忘了代码的本质是为了解决复杂的数学问题,从根源上去理解一种计算方法有助于我们对自身代码的优化,比如这些方法我们可以把它写到FPGA和CUDA等并行或者分布式的计算当中,加速我们的计算方法,这比直接单机去调用这些库会超乎想象的快。
「深度学习福利」大神带你进阶工程师,立即查看>>>
前后端的耦合想了很久,上下课都在思考怎么做,然后终于憋出来了。这是之前搞的一个视觉计算的项目,boss叫对接到前端,于是就产生了这样一个诡异的需求,就是前端打开摄像头,同时需要把摄像头的数据回传到后端进行图像处理(比如美颜啊脑袋上加个装饰品之类),这就需要涉及到前端和服务端的数据编码耦合,想了想既然任何图像在内存里面都是一个uchar矩阵,于是琢磨了这个东西出来。
一般情况下,图像在内存里的表达都是个uchar串,或者说byte流,因为我经常需要写跨语言调用的玩意儿,所以一般在内存里我都是用字符串和比特流进行交互,这里我采用了同样的思想,我们把opencv的图像进行编码为png,然后再一次编码为base64,通过websocket传输给前端。大致过程如下。
首先假设我们的前端打开websocket连接后端,连接上了以后前端打开摄像头取摄像头数据传输给后端,后端通过一系列的图像处理机器学习以后编码图像回传给前端。
前端代码: Title
C++服务器端(这里需要使用到websocket++读者请自行编译)
opencv_websocket_server.h // // Created by Pulsar on 2019/4/16. // #ifndef WEBSOCKETPP_OPENCV_WEBSOCKET_H #define WEBSOCKETPP_OPENCV_WEBSOCKET_H #include #include //#include #include #include #include typedef websocketpp::server WebsocketServer; typedef WebsocketServer::message_ptr message_ptr; class opencv_websocket { public: opencv_websocket(std::string file_path) ; void Run(int port); ~opencv_websocket(); }; #endif //WEBSOCKETPP_OPENCV_WEBSOCKET_H
opencv_websocket_server.cpp // // Created by Pulsar on 2019/4/16. // #include //using websocketpp::lib::placeholders::_1; //using websocketpp::lib::placeholders::_2; //using websocketpp::lib::bind; boost::shared_mutex read_write_mutex; boost::mutex lock; cv::CascadeClassifier cascade; //解码base64数据 static std::string base64Decode(const char *Data, int DataByte) { //解码表 const char DecodeTable[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, // '+' 0, 0, 0, 63, // '/' 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // '0'-'9' 0, 0, 0, 0, 0, 0, 0, 0, 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, // 'A'-'Z' 0, 0, 0, 0, 0, 0, 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, // 'a'-'z' }; std::string strDecode; int nValue; int i = 0; while (i < DataByte) { if (*Data != '\r' && *Data != '\n') { nValue = DecodeTable[*Data++] << 18; nValue += DecodeTable[*Data++] << 12; strDecode += (nValue & 0x00FF0000) >> 16; if (*Data != '=') { nValue += DecodeTable[*Data++] << 6; strDecode += (nValue & 0x0000FF00) >> 8; if (*Data != '=') { nValue += DecodeTable[*Data++]; strDecode += nValue & 0x000000FF; } } i += 4; } else { Data++; i++; } } return strDecode; } //编码base64数据 static std::string base64Encode(const unsigned char *Data, int DataByte) { //编码表 const char EncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; //返回值 std::string strEncode; unsigned char Tmp[4] = {0}; int LineLength = 0; for (int i = 0; i < (int) (DataByte / 3); i++) { Tmp[1] = *Data++; Tmp[2] = *Data++; Tmp[3] = *Data++; strEncode += EncodeTable[Tmp[1] >> 2]; strEncode += EncodeTable[((Tmp[1] << 4) | (Tmp[2] >> 4)) & 0x3F]; strEncode += EncodeTable[((Tmp[2] << 2) | (Tmp[3] >> 6)) & 0x3F]; strEncode += EncodeTable[Tmp[3] & 0x3F]; if (LineLength += 4, LineLength == 76) { strEncode += "\r\n"; LineLength = 0; } } //对剩余数据进行编码 int Mod = DataByte % 3; if (Mod == 1) { Tmp[1] = *Data++; strEncode += EncodeTable[(Tmp[1] & 0xFC) >> 2]; strEncode += EncodeTable[((Tmp[1] & 0x03) << 4)]; strEncode += "=="; } else if (Mod == 2) { Tmp[1] = *Data++; Tmp[2] = *Data++; strEncode += EncodeTable[(Tmp[1] & 0xFC) >> 2]; strEncode += EncodeTable[((Tmp[1] & 0x03) << 4) | ((Tmp[2] & 0xF0) >> 4)]; strEncode += EncodeTable[((Tmp[2] & 0x0F) << 2)]; strEncode += "="; } return strEncode; } //imgType 包括png bmp jpg jpeg等opencv能够进行编码解码的文件 static std::string Mat2Base64(const cv::Mat &img, std::string imgType) { //Mat转base64 std::string img_data; std::vector vecImg; std::vector vecCompression_params; vecCompression_params.push_back(CV_IMWRITE_JPEG_QUALITY); vecCompression_params.push_back(90); imgType = "." + imgType; //重点来了,它是负责把图像从opencv的Mat变成编码好的图像比特流的重要函数 cv::imencode(imgType, img, vecImg, vecCompression_params); img_data = base64Encode(vecImg.data(), vecImg.size()); return img_data; } //base64转Mat static cv::Mat Base2Mat(std::string &base64_data) { cv::Mat img; std::string s_mat; s_mat = base64Decode(base64_data.data(), base64_data.size()); std::vector base64_img(s_mat.begin(), s_mat.end()); img = cv::imdecode(base64_img, CV_LOAD_IMAGE_COLOR); return img; } void OnOpen(WebsocketServer *server, websocketpp::connection_hdl hdl) { std::cout << "have client connected" << std::endl; } void OnClose(WebsocketServer *server, websocketpp::connection_hdl hdl) { std::cout << "have client disconnected" << std::endl; } void OnMessage(WebsocketServer *server, websocketpp::connection_hdl hdl, message_ptr msg) { std::string image_str = msg->get_payload(); std::vector img_vec(image_str.begin(), image_str.end()); try { //把前端传来的图像字符串进行解码 cv::Mat img = cv::imdecode(img_vec, CV_LOAD_IMAGE_COLOR); if (!img.empty()) { // cv::imshow("", img); std::vector faces; lock.lock(); // cascade.detectMultiScale(img, faces, 1.1, 3, 0, cv::Size(30, 30)); // for (size_t t = 0; t < faces.size(); t++){ // cv::rectangle(img, faces[t], cv::Scalar(0, 0, 255), 2, 8); // } lock.unlock(); cv::Mat output = img; if (!output.empty()) { //把你处理完的图像转换为字符串返回给前端 std::string strRespon = Mat2Base64(output, "bmp"); server->send(hdl, strRespon, websocketpp::frame::opcode::text); } // cv::waitKey(10); } } catch (const std::exception &) { std::cout << " 解码异常" << std::endl; } } opencv_websocket::opencv_websocket(std::string file_path) { //训练好的文件名称,放置在可执行文件同目录下 if(!cascade.load(file_path))perror("Load Model Error"); } opencv_websocket::~opencv_websocket() { } void opencv_websocket::Run(int port) { WebsocketServer server; server.set_access_channels(websocketpp::log::alevel::all); server.clear_access_channels(websocketpp::log::alevel::frame_payload); // Initialize Asio server.init_asio(); // Register our message handler server.set_open_handler(websocketpp::lib::bind(&OnOpen, &server, ::websocketpp::lib::placeholders::_1)); server.set_close_handler(websocketpp::lib::bind(&OnClose, &server, websocketpp::lib::placeholders::_1)); server.set_message_handler(websocketpp::lib::bind(OnMessage, &server, websocketpp::lib::placeholders::_1, websocketpp::lib::placeholders::_2)); // Listen on port 9002 server.listen(port); // Start the server accept loop server.start_accept(); // Start the ASIO io_service run loop server.run(); } int main(int argc, char **argv) { std::cout<<"[INFO] load model"<上述工程地址:
https://gitee.com/Luciferearth/websocketpp
example\opencv_websocket_server下
注意websocket在Windows下需要改动编译依赖
去掉
iostream_server
testee_server
testee_client
utility_client
的Cmake(直接全部注释)
CmakeLists.txt set (WEBSOCKETPP_LIB ${WEBSOCKETPP_BUILD_ROOT}/lib)
后面加入以下编译命令 #########################################OpenSSL####################################### set(OPENSSL_INCLUDE_DIR D:/pgsql/include) set(OPENSSL_LIBRARIES D:/pgsql/lib/ssleay32MD.lib;D:/pgsql/lib/libeay32MD.lib) ####################################################################################### ##########################Windows 下对Boost的引用###################################### set(BUILD_EXAMPLES ON) set(BUILD_EXAMPLES ON) set(Boost_FOUND TRUE) set(Boost_INCLUDE_DIRS E:/local/boost_1_67_0) set(Boost_INCLUDE_DIR E:/local/boost_1_67_0) set(Boost_LIBRARY_DIRS E:/local/boost_1_67_0/lib64-msvc-14.0 ) set(Boost_LIBRARIES boost_filesystem-vc140-mt-x64-1_67.lib boost_filesystem-vc140-mt-gd-x64-1_67.lib libboost_zlib-vc140-mt-gd-x64-1_67.lib libboost_zlib-vc140-mt-x64-1_67.lib boost_system-vc140-mt-gd-x64-1_67.lib boost_system-vc140-mt-x64-1_67.lib libboost_chrono-vc140-mt-s-x64-1_67.lib libboost_chrono-vc140-mt-gd-x64-1_67.lib boost_thread-vc140-mt-gd-x64-1_67.lib boost_thread-vc140-mt-x64-1_67.lib ) ###################################################
opencv-server file (GLOB SOURCE_FILES *.cpp) file (GLOB HEADER_FILES *.hpp) set(OPENCV_INCLUDE_DIR F:/Smart_Classroom/3rdparty/ALLPLATHFORM/opencv/include) message(${OPENCV_INCLUDE_DIR}) set(OPENCV_LIB_DIR F:/Smart_Classroom/3rdparty/ALLPLATHFORM/opencv/x64/vc14/lib) message(${OPENCV_LIB_DIR}) include_directories(${OPENCV_INCLUDE_DIR}) link_directories(${OPENCV_LIB_DIR}) init_target (opencv_websocket_server) build_executable (${TARGET_NAME} ${SOURCE_FILES} ${HEADER_FILES}) file(COPY haarcascade_frontalface_alt.xml DESTINATION ${CMAKE_BINARY_DIR}/bin/) # link_boost () final_target () target_link_libraries(opencv_websocket_server opencv_world341.lib opencv_world341d.lib ) # set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "examples")
代码难免在打字的时候打错,有什么问题联系笔者。整个服务端的实现难点无非在于编码与解码的方法保持客户端和服务端数据耦合性,这个东西也琢磨了我好几天才琢磨透,再接再厉把,io真的是一个神奇的东西,当你把它深刻的理解到内存的时候,它就像个听话的孩子。
人工智能
2019-03-30 02:41:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
前言:
在2019年的3月之前我对铺天盖地的“区块链”网络风暴,都只是一个耳朵进另一个就帮忙给丢了...可3月中旬的时候一份工作让我决定去接触他,毕竟马大爷说过:在未来只有人工智能(AI)和区块链......我接到的第一个命令是做NEO(小蚁币)的冷钱包,可是我看了官网连签名都没的,更不要说什么冷钱包了,网上的资料收到的都是些没的多大用处的东西...好方啊不过入职的第一个任务就要逃避了吗?静下心来慢慢看吧?
在开发一个币,你一定要熟悉他的交易流程,那样你才不会走弯路...因为我走了太多了
第一步:下载客服端创建一个属于自己钱包
下载地址: https://github.com/neo-project/neo-gui/releases 我下载的: neo-gui-windows.zip (创建钱包那些就不说了呗,都是中文的,如果不会就放弃吧,把这步创建的钱包叫A)
创建钱包前吧钱包客服端变成测试网的参考: https://docs.neo.org/zh-cn/network/testnet.html
第二步:申请测试币
申请地址: https://neo.org/testcoin/apply 注意最后面的公钥一定要填第一步中创建钱包后的地址对应的公钥,这样你才有这个公钥,不然你拿了测试币用不了(我就是吃了这个亏的),申请好了你就去看看他需求文档那些吧,一直等到你收到测试B的邮件。
第三步:根据邮件创建多重签名地址和转账
第一步中已经有链接说过多重签名了转账了,你直接往你第一步中转。再往你新创建的钱包转,你发现这些都是不的出现没的私钥签名的步骤的,都是直接签名成功的。
现在重新创建一个新的钱包B,吧生成的地址干掉,直接创建合约地址。把里面的钱转到A,得到如图下
这一步很重要,拷贝出来保存好,后面分析数据,看明白别人的签名dome都有很大的帮助。
第四步:签名和广播
在第一步的钱包中进行签名(合约地址必须在哦,不然你的钱包和转账的地址没的关系,也是不能签名的)如图下:
你看交易就成功了。不过这个是客服端的,与我们要实现的离线签名关系不大啊,放心磨刀不误砍柴工工.....
第五步:下载大神的dome
我也在看这个地址的时候: https://www.jianshu.com/p/286c3cca3048 (里面有很的多东西)
在里面的社区爱好中找到了: Guil博士在NEO DevCon介绍Neow3j Java库
去下载项目: https://github.com/neow3j/neow3j
里面官方的api基本都是实现了的,也有没实现得哈
第六步:分析
这步就很重要的了,找到dome中的io.neow3j.examples.ransactions.CreateRawTransactionMultiSig.java,这个就是签名的东西了,就是我在客服端签名转账的java内容了....这个是个多重签名(这个就不多做解释了百度哈)现在研究一下这些数据是什么意思:
wif是钱包的私钥,通过钱包就可以查到,在io.neow3j.examples.utils.LoadWallet可以获得
ecKeyPair3是多余的可以干掉,如果你只签名一次,ecKeyPair2也可以干掉。
资产id的获取:neow3j.getAccountState
现在 说说:输出的地址是什么?我把客服端的交易拿来解析:地址: https://sdk.nel.group/# 解析签名钱的那个记录(我说保存好的那个)中的hex,放到链接中的交易解析,我开始以为是私钥,我加入了,签名ok,广播就错误了,多交易验证出错大致这个意思吧。在这墨迹了很长时间......于是重新看官方文档发型neo是UTXO(百度明白是什么意思)的,于是我查了地址信息:https://neoscan-testnet.io/api/test_net/v1/get_balance/加签名地址 发型了unspent.txid,估计就是他了,试了下,广播成功了,币交易成功了。与是我加入到我的离线签名项目中(只实现一次签名):
终结:完成离线签名 @RequestMapping("/neo/offline") public class NeoController { @Autowired public NeoInfo NeoInfo; @ApiOperation(value = "NEO离线签名", notes = "NEO离线签名") @RequestMapping(value="/sign", method = RequestMethod.GET) public String sign(NeoParam param) { ECKeyPair ecKeyPair = ECKeyPair.create(WIF.getPrivateKeyFromWIF(NeoInfo.getWif())); String multiSigAddress = Keys.getMultiSigAddress(1,ecKeyPair.getPublicKey()); RawVerificationScript verificationScript = Keys.getVerificationScriptFromPublicKey(1,ecKeyPair.getPublicKey()); RawTransaction rawTx = RawTransaction.createContractTransaction( null, null, Arrays.asList( new RawTransactionInput(param.getUnspentTxid(), 0) ), Arrays.asList( new RawTransactionOutput(0, param.getOutputAssetId(), param.getOutValue(), param.getInputAddress()), new RawTransactionOutput(1, param.getOutputAssetId(), param.getChangeValue(), multiSigAddress) ) ); // 序列化基本原始事务 // 重要:没有脚本! byte[] rawTxUnsignedArray = rawTx.toArray(); // 在三种可能的签名中添加两个签名——这里的顺序很重要! List rawInvocationScriptList = new ArrayList<>(); rawInvocationScriptList.add(new RawInvocationScript(Sign.signMessage(rawTxUnsignedArray, ecKeyPair))); rawTx.addScript(rawInvocationScriptList, verificationScript); byte[] rawTxSignedArray = rawTx.toArray(); return Numeric.toHexStringNoPrefix(rawTxSignedArray); } }
第一次接触数字货币,很多专业术语都不知道,走了很多的弯路,后续继续努力,开始别的币了.....
有开发钱包的加群:340697945,答案回 钱包,大家一起交流学习。
人工智能
2019-03-29 20:18:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
简介
汉语属于汉藏语系,与世界各国广泛使用的拼音文字相比,他更像一种古老的孤立语。
这是中华民族独特的地理位置和长期统一的发展历程所决定的。
虽然,汉语在历史上先后吸收和同化了匈奴、鲜卑、突厥、契丹、满、蒙古、梵语等语言中的许多成分,但是两千多年来,汉语特有的符号化表现形式却一直没有改变过。
1、文字符号的起源
只有了解历史,才能正确地理解现在,准确的预见未来。
因为文字土坯了语言的时空范围,是凝固的语言,所以文字并非从一开始就记录人们语言中的所有内容,而是由选择第记录一些对人们生产、生活比较重要的部分。千百年来,随着人们生产、生活不断发展,文字也在不断发展,并且促进了语言进一步的统一和规范。
这是人类语言发展的共同规律。
1.1、从记事谈起
结绳记事、岩画记事和刻契记事。这三类记事方式形成了后来文字符号的主要来源。 象形词典: http://www.vividict.com/default.aspx 《中国古文字的起源》 结绳计数,数字 考古发现虽然某些汉字的形体与结绳有零星的关系,但结绳记事不是汉字的主要来源; 绘画;东巴文; 刻契记事替代结绳记事是一种书写载体的革新,它使记事载体从一维发展到了二维; 有证据表明,刻契记事后来直接发展出了两河流域(底格里斯河和幼发拉底河流域)苏美尔人使用的楔形文字。
1.2、古文字的形成
夏王朝。
文字是一种记录语言的符号,原始的记录必须脱离了任意绘形、任意理解的阶段,产生一批具有约定的意义,具有固定读音的单字,并且可以开始进行语料积累的时候,才能算真的产生。
即文字的产生必须具备如下的三个特点: 约定的意义 基本固定的读音 基本一致的形式
形、音、义的三者一致性。
这就必须具备形、音、义三者一致性。必须对当时的季师傅好有一个再创造的过程,这个过程就是造字。
长时间集体累积和创作的产物,并在长时间的氏族扩张和衰亡的征战中不断传承与毁灭。
两种矛盾: 文字与语言; 文字与书写者;
2、六书及其他
后人在总结前人的造字方法时,最重要的成果就是六书。
象形、指事、会意、转注、假借、形声。
2.1、象形
“象形者,画成其物,随体诘诎(jie qu),日月是也。”
模仿和抽象。
其他的造字都是以象形文字为根,在此结构上做出某种变形。
2.2、指事
“指事者,视而可识,查而见意,上下是也。”
局部、整体。
2.3、会意
“会意者,比类合谊,以见指撝(hui,wei),武、信是也。”
两个或两个以上的度汉字,拼接。
2.4、形声
“形声者,以事为名,取譬相成,江河是也。”
90%;
在形声字之前,字形和字义是统一的,字形能够完整的表达语义,但从形声字开始,这种情况发生了变化。一部分表义的功能让位于表音的偏声旁,更便于将语言中表意的音节迅速构成文字。这使得文字在数量上发生了质的飞跃。同时,形声造字法使字形与字义逐渐分离开来。这是汉字走向符号化的第一步。
2.5、转注
转注是原始文字规范化的开始,转注暗示了这样一个规则——语义上近似的两个字,其字形也应该尽量相似,其不同之处可以通过其他造字模式来弥补。
例如:“女”通过加入表示“因生育而发达的两乳”的两点,即指事造字法,构造出了新字“母”。
转注法提出说明上文所述的4种造字法并不完整,表示同一语义的字可能有很多种。
殷商甲骨文:4500词就支持了丰富的社会生活,这就是转注造字法的作用。
2.6、假借
“假借者,本无其字,依声托事,令长是也。”
假借法使用已有的汉字去记录新词,其进步的意义在于,减少了需要记忆的字符数量。这是假借的积极作用。
但也因为这样导致了一字多意,客观上造成了一些同音同形而异义的词,使人不易掌握。
3、字形的流变
文字变革:隶变。
3.1、笔与墨的形成与变革
形成于商周,发展与秦汉。
3.2、隶变的方式
《试论汉字的隶变》
汉字的总体趋势是一个由繁到简的过程,这个趋势在中国上下五千年的历史汇总,从未改过。
3.3、汉字的符号化与结构
汉字新生了一种统一的子结构,就是我们常说的部首。
汉字最早部件化的开始。
楷书的通行加速了汉字符号化的进程,而文字研究在隶变之后又一次达到了新的高潮。
偏旁部首才是汉字构成的最小部分。
汉字结构 简介而不可分割 上下结构 左右结构 半包围结构 全包围结构 堆成结构 品字结构
我们在记忆汉字时,首先大度记忆数量小得多的偏旁部首,再根据各个偏旁部首的位置和结构来记忆各种生僻的汉字,就会使汉字的学习变得容易多了,从而及大地降低了汉字的学习难度,即便现在看来也是一种极为精妙的思维。
在隋唐之后,汉语书面语逐渐向口语化的趋势发展,后世称为近代百花。他的一个鲜明的特征就是与口语相结合,出现了大量的复音词和通俗的表达形式。此时,以独字为核心的古汉语又出现了新的发展。
人工智能
2019-03-29 17:47:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
简介
NLP.
1、历史回顾
1、从科幻到现实
2、早期的探索
3、规则派还是统计派
4、从机器学习到认知计算
2、现代自然语言系统简介
1、NLP流程与开源框架
2、哈工大NLP平台及其演示环境
3、Stanford NLP团队及其演示环境
4、NLTK开发环境
3、整合中文分词模块
1、安装Ltp Python组件
2、使用Ltp3.3进行中文分词
3、使用结巴分词模块
4、整合词性标注模块
1、Ltp 3.3词性标注
2、安装StanfordNLP并编写Python接口类
3、执行Stanford词性标注
5、整合命名实体识别模块
1、Ltp 3.3命名实体识别
2、Stanford命名实体识别
6、整合句法解析模块
1、Ltp 3.3句法依存树
2、Stanford Parser类
3、Stanford短语结构树
4、Stanford依存句法树
7、整合语义角色标注模块
8、结语
人工智能
2019-03-29 17:46:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
3月29日,德恩精工科技股份有限公司宣布和阿里云合作已取得阶段性成果。通过阿里云中台技术应用,公司的销售、库存管理、生产排程、智能供应链等都有不同程度的提升。
德恩精工是国家级高新技术企业,生产范围包括大规模定制化皮带轮、齿轮、链轮、减速电机等标准传动件产品,以及床身、主轴箱、齿轮箱等非标定制件产品。公司产品远销欧洲、美国、日韩等国家。
作为典型的“多品种、小批量、全工序”离散型制造企业,德恩精工同样存在着该行业普遍面临的“三高”难题——“高交期、高库存、高成本”。2018年3月,德恩精工和阿里云签署了框架合作协议,双方开始探索以数字技术解决“三高”难题。
据了解,双方合作构建了云上数据中台,打通了销售、生产、研发、库存、物流等原本分隔的IT系统,实现了企业数据的存储、汇集和共享,从而提高上述环节的效率。以工厂的“生产排程”为例,通过设备物联网采集反映的适时制造产能数据,人工智能算法综合计算订单交期、订单优先级、工艺成组技术、生产中心历史良品率等数据,从而实现了订单的交付峰值以及销售额预测。
此外针对“高交期”问题,双方合作对销售订单历史数据进行分析建模,结合天气、季节、国际贸易环境、国别差异、线上大促等因素对头部产品的销售订单进行预测,进而指导预生产、预备货,来实现降低库存成本,缩短供货交期。
中台是阿里巴巴于2015年提出的企业架构理念。利用“业务中台”,盒马鲜生、钉钉、飞猪等创新业务前端部门可以通过平台上的产品技术模块,像搭积木一样快速搭建起来。“数据中台”则打破了不同业务部门之间的烟囱式IT架构,带来了持续的高效创新。近年来,阿里云利用中台的架构及产品帮助众多企业实现数字化转型。
原文链接
人工智能
2019-03-29 13:23:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
汉字转拼音
HanLP中的汉字转拼音功能也十分的强大。
说明 : HanLP不仅支持基础的汉字转拼音,还支持声母、韵母、音调、音标和输入法首字母首声母功能。 HanLP能够识别多音字,也能给繁体中文注拼音。 最重要的是,HanLP采用的模式匹配升级到AhoCorasickDoubleArrayTrie,性能大幅提升,能够提供毫秒级的响应速度!
算法详解 : 《汉字转拼音与简繁转换的Java实现》 # 汉字转拼音 Pinyin = JClass("com.hankcs.hanlp.dictionary.py.Pinyin") text = "重载不是重任!" pinyin_list = HanLP.convertToPinyinList(text)
print("原文,", end=" ") print(text) print("拼音(数字音调),", end=" ") print(pinyin_list) print("拼音(符号音调),", end=" ") for pinyin in pinyin_list: print("%s," % pinyin.getPinyinWithToneMark(), end=" ") print("\n拼音(无音调),", end=" ") for pinyin in pinyin_list: print("%s," % pinyin.getPinyinWithoutTone(), end=" ") print("\n声调,", end=" ") for pinyin in pinyin_list: print("%s," % pinyin.getTone(), end=" ") print("\n声母,", end=" ") for pinyin in pinyin_list: print("%s," % pinyin.getShengmu(), end=" ") print("\n韵母,", end=" ") for pinyin in pinyin_list: print("%s," % pinyin.getYunmu(), end=" ") print("\n输入法头,", end=" ") for pinyin in pinyin_list: print("%s," % pinyin.getHead(), end=" ")
print() # 拼音转换可选保留无拼音的原字符 print(HanLP.convertToPinyinString("截至2012年,", " ", True)) print(HanLP.convertToPinyinString("截至2012年,", " ", False))

1.原文, 重载不是重任!
2.拼音(数字音调), [chong2, zai3, bu2, shi4, zhong4, ren4, none5]
3.拼音(符号音调), chóng, zǎi, bú, shì, zhòng, rèn, none,
4.拼音(无音调), chong, zai, bu, shi, zhong, ren, none,
5.声调, 2, 3, 2, 4, 4, 4, 5,
6.声母, ch, z, b, sh, zh, r, none,
7.韵母, ong, ai, u, i, ong, en, none,
8.输入法头, ch, z, b, sh, zh, r, none,
9.jie zhi none none none none nian none
10.jie zhi 2 0 1 2 nian ,
拼音转中文
HanLP中的数据结构和接口是灵活的,组合这些接口,可以自己创造新功能,我们可以使用AhoCorasickDoubleArrayTrie实现的最长分词器,需要用户调用setTrie()提供一个AhoCorasickDoubleArrayTrie

1.StringDictionary = JClass(
2."com.hankcs.hanlp.corpus.dictionary.StringDictionary")
3.CommonAhoCorasickDoubleArrayTrieSegment = JClass(
4."com.hankcs.hanlp.seg.Other.CommonAhoCorasickDoubleArrayTrieSegment")
5.Config = JClass("com.hankcs.hanlp.HanLP$Config")
6.
7.TreeMap = JClass("java.util.TreeMap")
8.TreeSet = JClass("java.util.TreeSet")
9.
10.dictionary = StringDictionary()
11.dictionary.load(Config.PinyinDictionaryPath)
12.entry = {}
13.m_map = TreeMap()
14.for entry in dictionary.entrySet():
15.pinyins = entry.getValue().replace("[\\d,]", "")
16.words = m_map.get(pinyins)
17.if words is None:
18.words = TreeSet()
19.m_map.put(pinyins, words)
20.words.add(entry.getKey())
21.words = TreeSet()
22.words.add("绿色")
23.words.add("滤色")
24.m_map.put("lvse", words)
25.
26.segment = CommonAhoCorasickDoubleArrayTrieSegment(m_map)
27.print(segment.segment("renmenrenweiyalujiangbujianlvse"))
28.print(segment.segment("lvsehaihaodajiadongxidayinji"))

1.[renmenrenweiyalujiangbujian/null, lvse/[滤色, 绿色]]
2.[lvse/[滤色, 绿色], haihaodajiadongxidayinji/null]

字符正则化
演示正规化字符配置项的效果(繁体->简体,全角->半角,大写->小写)。
该配置项位于hanlp.properties中,通过Normalization=true来开启(现在直接通过HanLP.Config.Normalization开启即可)。

切换配置后必须删除CustomDictionary.txt.bin缓存,否则只影响动态插入的新词。
在我动笔前一个星期,已经有同学添加了,添加自定义词典之后,自动删除缓存的功能。地址请参阅github.com/hankcs/HanLP/pull/954,现在只需要开启正则化即可

1.CustomDictionary =JClass("com.hankcs.hanlp.dictionary.CustomDictionary")
2.print("HanLP.Config.Normalization = False\n")
3.HanLP.Config.Normalization = False
4.CustomDictionary.insert("爱听4G", "nz 1000")
5.print(HanLP.segment("爱听4g"))
6.print(HanLP.segment("爱听4G"))
7.print(HanLP.segment("爱听4G"))
8.print(HanLP.segment("爱听4G"))
9.print(HanLP.segment("愛聽4G"))
10.
11.print(HanLP.segment("喜欢4G"))
12.print(HanLP.segment("hankcs在臺灣寫代碼"))

13.
14.print("\nHanLP.Config.Normalization = True\n")
15.HanLP.Config.Normalization = True
16.print(HanLP.segment("爱听4g"))
17.print(HanLP.segment("爱听4G"))
18.print(HanLP.segment("爱听4G"))
19.print(HanLP.segment("爱听4G"))
20.print(HanLP.segment("愛聽4G"))
21.
22.print(HanLP.segment("喜欢4G"))
23.print(HanLP.segment("hankcs在臺灣寫代碼"))
24.
25.HanLP.Config.ShowTermNature = False
27.text = HanLP.s2tw("现在的HanLP已经添加了添加自定义词典之后,自动删除缓存的功能,现在只需要开启正则化即可")
28.print(text)
29.print(HanLP.segment(text))
30.HanLP.Config.ShowTermNature = False

1.HanLP.Config.Normalization = False
2.
3.[爱听4g]
4.[爱听4G]
5.[爱, 听, 4, G]
6.[爱, 听, 4, G]
7.[愛, 聽, 4, G]
8.[喜欢, 4, G]
9.[hankcs, 在, 臺, 灣寫, 代, 碼]
10.
11.HanLP.Config.Normalization = True
12.
13.[爱听4g]
14.[爱听4g]
15.[爱听4g]
16.[爱听4g]
17.[爱听4g]
18.[喜欢, 4, g]
19.[hankcs, 在, 台湾, 写, 代码]
20.現在的HanLP已經新增了新增自定義詞典之後,自動刪除快取的功能,現在只需要開啟正則化即可
21.[现在, 的, hanlp, 已经, 新增, 了, 新增, 自定义, 词典, 之后, ,, 自动, 删除, 快, 取, 的
---------------------
人工智能
2019-03-29 11:45:00
「深度学习福利」大神带你进阶工程师,立即查看>>> public class FaceUtil { private static final String TAG = FaceUtil.class.getSimpleName(); private static FaceUtil faceInstance = null; public FaceDB mFaceDB; private AFR_FSDKFace mAFR_FSDKFace; private static Context mContext = null; //face detect private AFT_FSDKVersion version = new AFT_FSDKVersion(); private AFT_FSDKEngine engine = new AFT_FSDKEngine(); private ASAE_FSDKVersion mAgeVersion = new ASAE_FSDKVersion(); private ASAE_FSDKEngine mAgeEngine = new ASAE_FSDKEngine(); private ASGE_FSDKVersion mGenderVersion = new ASGE_FSDKVersion(); private ASGE_FSDKEngine mGenderEngine = new ASGE_FSDKEngine(); private List result = new ArrayList<>(); private List ages = new ArrayList<>(); private List genders = new ArrayList<>(); private AFT_FSDKFace mAFT_FSDKFace = null; private boolean openFace = false; public static FaceUtil getInstance(Context context) { if(faceInstance == null) { faceInstance = new FaceUtil(context); } return faceInstance; } public FaceUtil(Context context) { mContext = context; } public void setFaceDB() { //face mFaceDB = new FaceDB(mContext.getExternalCacheDir().getPath()); Log.d(TAG,"getExternalCacheDir : "+mContext.getExternalCacheDir().getPath()); } public void initFaceDetect() { AFT_FSDKError err = engine.AFT_FSDK_InitialFaceEngine(FaceDB.appid, FaceDB.ft_key, AFT_FSDKEngine.AFT_OPF_0_HIGHER_EXT, 16, 5); Log.d(TAG, "AFT_FSDK_InitialFaceEngine =" + err.getCode()); err = engine.AFT_FSDK_GetVersion(version); Log.d(TAG, "AFT_FSDK_GetVersion:" + version.toString() + "," + err.getCode()); ASAE_FSDKError error = mAgeEngine.ASAE_FSDK_InitAgeEngine(FaceDB.appid, FaceDB.age_key); Log.d(TAG, "ASAE_FSDK_InitAgeEngine =" + error.getCode()); error = mAgeEngine.ASAE_FSDK_GetVersion(mAgeVersion); Log.d(TAG, "ASAE_FSDK_GetVersion:" + mAgeVersion.toString() + "," + error.getCode()); ASGE_FSDKError error1 = mGenderEngine.ASGE_FSDK_InitgGenderEngine(FaceDB.appid, FaceDB.gender_key); Log.d(TAG, "ASGE_FSDK_InitgGenderEngine =" + error1.getCode()); error1 = mGenderEngine.ASGE_FSDK_GetVersion(mGenderVersion); Log.d(TAG, "ASGE_FSDK_GetVersion:" + mGenderVersion.toString() + "," + error1.getCode()); } public void deInitFaceDetect() { AFT_FSDKError err = engine.AFT_FSDK_UninitialFaceEngine(); Log.d(TAG, "AFT_FSDK_UninitialFaceEngine =" + err.getCode()); ASAE_FSDKError err1 = mAgeEngine.ASAE_FSDK_UninitAgeEngine(); Log.d(TAG, "ASAE_FSDK_UninitAgeEngine =" + err1.getCode()); ASGE_FSDKError err2 = mGenderEngine.ASGE_FSDK_UninitGenderEngine(); Log.d(TAG, "ASGE_FSDK_UninitGenderEngine =" + err2.getCode()); } public boolean faceFeatureDetect(byte[] mYUVData,int width,int height) { boolean bDectectFace = false; AFT_FSDKError err =engine.AFT_FSDK_FaceFeatureDetect(mYUVData, width, height, AFT_FSDKEngine.CP_PAF_NV21, result); Log.d(TAG, "AFT_FSDK_FaceFeatureDetect =" + err.getCode()); Log.d(TAG, "Face=" + result.size()); if (!result.isEmpty()) { for (AFT_FSDKFace face : result) { Log.d(TAG, "Face:" + face.toString()); } mAFT_FSDKFace = result.get(0).clone(); bDectectFace = true; } else { Log.d(TAG,"没有检测到人脸"); bDectectFace = false; } result.clear(); return bDectectFace; } public AFT_FSDKFace getmAFT_FSDKFace() { return mAFT_FSDKFace; } public String getAge(byte[] mYUVData,int width,int height,List face1) { String age; ASAE_FSDKError error1 = mAgeEngine.ASAE_FSDK_AgeEstimation_Image(mYUVData, width, height, AFT_FSDKEngine.CP_PAF_NV21, face1, ages); Log.d("face", "ASAE_FSDK_AgeEstimation_Image:" + error1.getCode()); if(ages.size()== 0) { Log.d(TAG, "ages.size()== 0" ); age = "年龄未知"; } else { Log.d(TAG, "age:" + ages.get(0).getAge()); age = ages.get(0).getAge() == 0 ? "年龄未知" : ages.get(0).getAge() + "岁"; } return age; } public String getGender(byte[] mYUVData,int width,int height,List face2) { String gender; ASGE_FSDKError error2 = mGenderEngine.ASGE_FSDK_GenderEstimation_Image(mYUVData, width, height, AFT_FSDKEngine.CP_PAF_NV21, face2, genders); Log.d("face", ",ASGE_FSDK_GenderEstimation_Image:" + error2.getCode()); if(ages.size()== 0) { Log.d(TAG, "ages.size()== 0" ); gender = "性别未知"; } else { Log.d(TAG, "gender:" + genders.get(0).getGender()); gender = genders.get(0).getGender() == -1 ? "性别未知" : (genders.get(0).getGender() == 0 ? "男" : "女"); } return gender; } /** * @param path * @return */ public static Bitmap decodeImage(String path) { Bitmap res; try { ExifInterface exif = new ExifInterface(path); int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); BitmapFactory.Options op = new BitmapFactory.Options(); op.inSampleSize = 1; op.inJustDecodeBounds = false; //op.inMutable = true; res = BitmapFactory.decodeFile(path, op); //rotate and scale. Matrix matrix = new Matrix(); if (orientation == ExifInterface.ORIENTATION_ROTATE_90) { matrix.postRotate(90); } else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) { matrix.postRotate(180); } else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) { matrix.postRotate(270); } Bitmap temp = Bitmap.createBitmap(res, 0, 0, res.getWidth(), res.getHeight(), matrix, true); Log.d(TAG, "check target Image:" + temp.getWidth() + "X" + temp.getHeight()); if (!temp.equals(res)) { res.recycle(); } return temp; } catch (Exception e) { e.printStackTrace(); } return null; } public void bitmapDetect(String name,Bitmap mBitmap) { byte[] data = new byte[mBitmap.getWidth() * mBitmap.getHeight() * 3 / 2]; ImageConverter convert = new ImageConverter(); convert.initial(mBitmap.getWidth(), mBitmap.getHeight(), ImageConverter.CP_PAF_NV21); if (convert.convert(mBitmap, data)) { Log.d(TAG, "convert ok!"); } convert.destroy(); AFD_FSDKEngine engine = new AFD_FSDKEngine(); AFD_FSDKVersion version = new AFD_FSDKVersion(); List result = new ArrayList(); AFD_FSDKError err = engine.AFD_FSDK_InitialFaceEngine(FaceDB.appid, FaceDB.fd_key, AFD_FSDKEngine.AFD_OPF_0_HIGHER_EXT, 16, 5); Log.d(TAG, "AFD_FSDK_InitialFaceEngine = " + err.getCode()); if (err.getCode() != AFD_FSDKError.MOK) { // Message reg = Message.obtain(); // reg.what = MSG_CODE; // reg.arg1 = MSG_EVENT_FD_ERROR; // reg.arg2 = err.getCode(); // mUIHandler.sendMessage(reg); Log.d(TAG, "MSG_EVENT_FD_ERROR"); ToastUtil.showToast("sdk error :MSG_EVENT_FD_ERROR "+err.getCode()); } err = engine.AFD_FSDK_GetVersion(version); Log.d(TAG, "AFD_FSDK_GetVersion =" + version.toString() + ", " + err.getCode()); err = engine.AFD_FSDK_StillImageFaceDetection(data, mBitmap.getWidth(), mBitmap.getHeight(), AFD_FSDKEngine.CP_PAF_NV21, result); Log.d(TAG, "AFD_FSDK_StillImageFaceDetection =" + err.getCode() + "<" + result.size()); if (!result.isEmpty()) { AFR_FSDKVersion version1 = new AFR_FSDKVersion(); AFR_FSDKEngine engine1 = new AFR_FSDKEngine(); AFR_FSDKFace result1 = new AFR_FSDKFace(); AFR_FSDKError error1 = engine1.AFR_FSDK_InitialEngine(FaceDB.appid, FaceDB.fr_key); Log.d(TAG, "AFR_FSDK_InitialEngine = " + error1.getCode()); if (error1.getCode() != AFD_FSDKError.MOK) { // Message reg = Message.obtain(); // reg.what = MSG_CODE; // reg.arg1 = MSG_EVENT_FR_ERROR; // reg.arg2 = error1.getCode(); // mUIHandler.sendMessage(reg); Log.d(TAG, "MSG_EVENT_FR_ERROR"); ToastUtil.showToast("sdk error :MSG_EVENT_FR_ERROR "+error1.getCode()); } error1 = engine1.AFR_FSDK_GetVersion(version1); Log.d(TAG, "FR=" + version.toString() + "," + error1.getCode()); //(210, 178 - 478, 446), degree = 1 780, 2208 - 1942, 3370 error1 = engine1.AFR_FSDK_ExtractFRFeature(data, mBitmap.getWidth(), mBitmap.getHeight(), AFR_FSDKEngine.CP_PAF_NV21, new Rect(result.get(0).getRect()), result.get(0).getDegree(), result1); Log.d(TAG, "Face=" + result1.getFeatureData()[0] + "," + result1.getFeatureData()[1] + "," + result1.getFeatureData()[2] + "," + error1.getCode()); if(error1.getCode() == error1.MOK) { mAFR_FSDKFace = result1.clone(); // int width = result.get(0).getRect().width(); // int height = result.get(0).getRect().height(); // Bitmap face_bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); // Canvas face_canvas = new Canvas(face_bitmap); // face_canvas.drawBitmap(mBitmap, result.get(0).getRect(), new Rect(0, 0, width, height), null); // Message reg = Message.obtain(); // reg.what = MSG_CODE; // reg.arg1 = MSG_EVENT_REG; // reg.obj = face_bitmap; // mUIHandler.sendMessage(reg); mFaceDB.addFace(name, mAFR_FSDKFace); Log.d(TAG, "addFace MSG_EVENT_REG jxd"); ToastUtil.showToast(mContext.getString(R.string.register_face_success)+name); } else { // Message reg = Message.obtain(); // reg.what = MSG_CODE; // reg.arg1 = MSG_EVENT_NO_FEATURE; // mUIHandler.sendMessage(reg); Log.d(TAG, "MSG_EVENT_NO_FEATURE"); ToastUtil.showToast(mContext.getString(R.string.no_feature)); } error1 = engine1.AFR_FSDK_UninitialEngine(); Log.d(TAG, "AFR_FSDK_UninitialEngine : " + error1.getCode()); } else { // Message reg = Message.obtain(); // reg.what = MSG_CODE; // reg.arg1 = MSG_EVENT_NO_FACE; // mUIHandler.sendMessage(reg); Log.d(TAG, "MSG_EVENT_NO_FACE"); ToastUtil.showToast(mContext.getString(R.string.no_face)); } err = engine.AFD_FSDK_UninitialFaceEngine(); Log.d(TAG, "AFD_FSDK_UninitialFaceEngine =" + err.getCode()); } public static boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is DownloadsProvider. */ public static boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is MediaProvider. */ public static boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri.getAuthority()); } /** * Get the value of the data column for this Uri. This is useful for * MediaStore Uris, and other file-based ContentProviders. * * @param context The context. * @param uri The Uri to query. * @param selection (Optional) Filter used in the query. * @param selectionArgs (Optional) Selection arguments used in the query. * @return The value of the _data column, which is typically a file path. */ public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { Cursor cursor = null; final String column = "_data"; final String[] projection = { column }; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { final int index = cursor.getColumnIndexOrThrow(column); return cursor.getString(index); } } finally { if (cursor != null) cursor.close(); } return null; } public String getPath(Uri uri) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if (DocumentsContract.isDocumentUri(mContext, uri)) { // ExternalStorageProvider if (isExternalStorageDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; if ("primary".equalsIgnoreCase(type)) { return Environment.getExternalStorageDirectory() + "/" + split[1]; } // TODO handle non-primary volumes } else if (isDownloadsDocument(uri)) { final String id = DocumentsContract.getDocumentId(uri); final Uri contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); return getDataColumn(mContext, contentUri, null, null); } else if (isMediaDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; Uri contentUri = null; if ("image".equals(type)) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ("video".equals(type)) { contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } else if ("audio".equals(type)) { contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } final String selection = "_id=?"; final String[] selectionArgs = new String[] { split[1] }; return getDataColumn(mContext, contentUri, selection, selectionArgs); } } } String[] proj = { MediaStore.Images.Media.DATA }; Cursor actualimagecursor = mContext.getContentResolver().query(uri, proj, null, null, null); int actual_image_column_index = actualimagecursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); actualimagecursor.moveToFirst(); String img_path = actualimagecursor.getString(actual_image_column_index); String end = img_path.substring(img_path.length() - 4); if (0 != end.compareToIgnoreCase(".jpg") && 0 != end.compareToIgnoreCase(".png")) { return null; } return img_path; } public void setFaceFunction() { openFace = SharedPreferenceUtil.getBoolean(OPEN_FACE, Constant.FACE, false); openFace = !openFace; SharedPreferenceUtil.putBoolean(OPEN_FACE, Constant.FACE, openFace); if(openFace) { ToastUtil.showToast(R.string.open_face); } else { ToastUtil.showToast(R.string.close_face); } } public boolean getFaceIsOpen() { return openFace; } }
人工智能
2019-03-22 15:15:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
随着深度学习的兴起,越来越多的人从事算法工程师这一岗位。有时候他们自嘲自己为“天桥调参师”。当然,算法工程师的工作内容并没有那么简单,因为设置超参数的过程需要专业知识和广泛的试验和试错过程。尤其是针对学习率(learning rate)、批量大小(batch size)、动量( momentum)和权重衰减(weight decay)这些超参数而言,没有简单易行的方法来设置。
深度学习模型充满了超参数,在如此高维空间中找到这些参数的最佳值并不是一项容易的挑战。在讨论找到最佳超参数的方法之前,首先了解这些超参数:学习率、批量大小、动量和权重衰减。这些超参数类似于开关旋钮,可以在模型训练期间进行调整。为了使得模型能够获得最佳结果,需要找到这些超参数的最佳值。
梯度下降
梯度下降是训练机器学习算法中常用的优化技术。训练机器学习算法的主要目的是调整权重w以最小化损失函数或成本函数。通过最小化成本函数,就可以找到产生最佳模型性能的参数[1]。
回归问题的典型损失函数图类似于碗的形状,如下所示。
在梯度下降算法中,首先随机模型参数,并计算每次学习迭代的误差,不断更新模型参数以更接近导致最小成本的值。梯度下降算法将梯度乘以一个标量(学习率),以确定下一个点。
如果用dW和db作为更新参数W和b的梯度,梯度下降算法如下:
如果学习率很小,那么训练会更加可靠,但花费的时间也更多,因为每次移动的步长很小。
如果学习率很大,那么训练可能不收敛。权重变化可能很大,以至于优化器错失最优化并使得损失变大。因此,最终目标是找到可以快速获得最小损失的最佳学习率。
一般而言,可以将梯度下降视为在山谷中滚动的球。希望它能够在停留山脉的最深处,但有时可能会出错。
根据球开始滚动的位置,它可能会停留在山谷的底部。但不是最低的一个,这称为局部最小值。初始化模型权重的方式可能会导致局部最小值。为了避免这种情况,可以随机始化权重向量。
用2-D表示损失面,如下所示:
红点是全局最小值,希望能够达到这一点。使用梯度下降方法,更新将如下所示:
随着梯度下降的每次迭代,向上和向下振荡移动到局部最优。如果使用更大的学习率,那么垂直振荡将具有更高的幅度。这种垂直振荡会减慢梯度下降过程,并阻止设置更大的学习率,而学习速率太小会使梯度下降变慢。 目标是希望在垂直方向上学习更慢,在水平方向上学习更快,这将有助于更快地达到全局最小值。
为了实现这一点,可以使用具有动量的梯度下降 [2]。
梯度下降:
在动量方面,采用dW和db的指数加权平均值,而不是每个时期单独使用dW和db。
其中 β 是另一个称为动量的超参数,取值范围从0到1。它设置先前值的平均值与当前值之间的权重,以计算新的加权平均值。
计算指数加权平均值后更新参数。
通过使用dW和db的指数加权平均值,将垂直方向上的振荡平均化为接近零。然而,在水平方向上,所有导数都指向水平方向的右侧,因此水平方向上的平均值仍然相当大。它允许算法采用更直接的路径朝向局部最优并阻尼垂直振荡。基于此,算法最终会在局部最优处进行几次迭代。
有三种梯度下降的方法:
批量梯度下降(Batch gradient descent: ) 使用所有的训练实例来更新每次迭代中的模型参数; 通过准确估计误差梯度来缓慢收敛;
随机梯度下降(Stochastic Gradient Descent) 在每次迭代中仅使用单个训练实例更新参数。训练实例通常是随机选择的; 通过估计错误梯度快速收敛;
小批量梯度下降(Mini-batch Gradient Descent) 一次取 b 个示例:训练时不是使用所有的示例,而是将训练集划分为称为批处理的较小尺寸,每次取 b 个示例用来更新模型参数; 小批量梯度下降试图在随机梯度下降的稳健性和批量梯度下降的效率之间找到平衡; 小批量梯度下降是深度学习领域中最常用的梯度下降方法。缺点是它引入了额外的超参数'b';
搜索最佳配置的方法:网格搜索和随机搜索
网格搜索
在网格搜索[3]中,尝试每个可能的参数配置。
步骤: 定义一个n维网格,其中每个都为超参数映射。例如 n =(learning_rate, batch_size) ; 对于每个维度,定义可能值的范围:例如 batch_size = [4,8,16,32] , learning_rate = [0.1,0.01,0.0001] ; 搜索所有可能的配置并等待结果建立最佳配置:例如 C1 =(0.1,4) - > acc = 92% , C2 =(0.01,4) - > acc = 92.3% 等;
随着维度的增多,搜索将在时间复杂度上发生爆炸。当维度小于或等于4时,通常使用这种方法。虽然它最终能保证找到最佳配置,但它仍然不是优选的,最好是使用随机搜索。
随机搜索
随机搜索[4]首先从配置空间中随机选取一个点,使用随机搜索更广泛地探索超参数空间。这可以在更少的迭代次数中找到最佳配置。例如:
在网格布局中,很容易注意到,即使已经训练了9个模型,而每个变量只使用了3个值。然而,使用随机搜索,我们不太可能不止一次地选择相同的变量,将使用9个不同的值为每个变量训练9个模型。更多详细分析,请参阅 该文 。
尽管随机搜索比网格搜索表现更好,但这两种方法在计算上仍然是昂贵且耗时的。在2018年,Leslie在其经典论文中提出了关于识别最佳超参数的各种方法的详细报告[5]。其中最好的方法是基于通过检查测试/验证损失以寻找欠拟合和过拟合的曲线来找到二者之间的平衡,以便争取最佳的超参数集合。 超参数调整过程无异于在钢丝上走路,以实现欠拟合和过拟合之间的平衡。
方法 1.通过在训练早期监控验证/测试损失,观察分析训练曲线,通过几个时期来调整模型结构和超参数; 2.在训练过程早期测试或验证损失的欠拟合或过拟合对于调整超参数是有用的;
寻找最佳超参数
学习率(LR)
如果学习率太小,则可能发生过拟合。较高的学习率有助于正则训练,但如果学习率过大,训练就会出现误差。因此,可以进行短距离网格搜索以找到收敛或发散的学习率,但还有另一种方法称为“周期性学习率(CLR)”。
实验表明,训练期间使用不同的学习率总体上是有益的,因此建议在一个取值范围内周期性地改变学习率,而不是将其设定为固定值。让学习率在一定范围内变化,而不是采用逐步、固定或指数级减少学习率值。即设置好最小和最大边界,学习率在这些边界之间循环变化。
如何估算合理的最小和最大边界值?
LR范围测试:运行模型几个epoch,同时让学习率在高低学习率值之间线性增加。对于浅层的3层架构,最大设置为 0.01 ,而对于resnet这样的网络,学习率最大可以设置为 3.0 。
从一轮循环确定最大学习速率,并将最大值的十分之一作为最小学习率的表现也不错[6]。
批量大小(Batch size)
与学习率不同,其值不影响计算训练时间。批量大小受硬件内存的限制,而学习率则不然。建议使用适合硬件内存的较大批量大小,并使用更大的学习速率。
如果服务器有多个GPU,则总批量大小是单个GPU上的批量大小乘以GPU的数量。
周期性动量(Cyclical Momentum)
动量和学习率密切相关。最佳学习率取决于动量,而动量又取决于学习率。与学习率一样,在不引起训练不稳定的情况下尽可能设置大的动量值是很有价值的。
查找学习率和动量组合的步骤 使用循环学习率:最佳训练步骤是循环增加学习率,初始化一个小的学习率,使其开始收敛,并减少周期动量。当学习率增加时,使用递减的循环动量加快收敛并且当稳定训练后,并设置更大的学习率;
左:学习率周期,右:动量周期 使用恒定学习率:如果使用恒定的学习率,那么大的恒定动量(即 0.9-0.99 )将起到伪增加学习率的作用并加速训练。但是,使用过大的动量值会导致训练结果很差。
无论是循环学习速率还是恒定学习速率,可以尝试在0.9到0.99范围内设定动量值,并从中选择一个表现最佳值。
权重衰减
体重衰减是正则化的一种形式,它在训练中起着重要作用,因此需要适当设定[7]。权重衰减被定义为将每个时期的梯度下降中的每个权重乘以因子λ( 0 <λ<1 )。
一般而言,可以测试权重衰减值为 1 /10³ 、 1 /10⁴ 、 1 /10⁵ 和 0 。较小的数据集和模型结构设置较大的权重衰减值,而较大的数据集和更深的模型结构设置较小的值。
如果使用恒定的学习率而不是使用学习率范围进行搜索,则最佳权重衰减会有所不同。由于较大的学习率提供正则化,因此较小的权重衰减值是最佳的。
总结
学习率: 执行学习率范围测试以确定“大”的学习率。
*一轮测试确定最大学习速率,将最小学习速率设置为最大学习速率的十分之一。
动量: 用短期动量值 0.99 、 0.97 、 0.95 和 0.9 进行测试,以获得动量的最佳值; 如果使用周期学习率计划,最好从该最大动量值开始循环设置动量,并随着学习率的增加而减小到 0.8 或 0.85 ;
批量大小: 根据硬件条件使用尽可能大的批量大小,然后比较不同批量大小的性能; 小批量添加正规化的效果大,而大批量添加的正则化效果小,因此在适当平衡正规化效果的同时利用好它; 使用更大的批量通常会更好,这样就可以使用更大的学习率;
权重衰减: 网格搜索以确定适当的幅度,但通常不需要超过一个有效数字精度; 更复杂的数据集需要较少的正则化,因此设置为较小的权重衰减值,例如 10^-4 、 10^-5 、 10^-6 、 0 ; 浅层结构需要更多的正则化,因此设置更大的权重衰减值,例如 10^-2 、 10^-3 、 10^-4 ;
参考文献:
[1] https://www.jeremyjordan.me/gradient-descent/
[2] https://engmrk.com/gradient-descent-with-momentum/
[3] https://blog.floydhub.com/guide-to-hyperparameters-search-for-deep-learning-models/
[4] http://www.jmlr.org/papers/volume13/bergstra12a/bergstra12a.pdf
[5] https://arxiv.org/pdf/1803.09820.pdf
[6] https://arxiv.org/pdf/1506.01186.pdf
[7] https://papers.nips.cc/paper/563-a-simple-weight-decay-can-improve-generalization.pdf
[8] https://www.analyticsvidhya.com/blog/2018/11/neural-networks-hyperparameter-tuning-regularization-deeplearning/
作者:【方向】
原文链接
本文为云栖社区原创内容,未经允许不得转载。
人工智能
2019-03-22 11:03:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
Hanlp1.7版本在去年下半年的时候就随大快的DKH1.6版本同时发布了,截至目前1.7大版本也更新到了1.7.1了。本篇分别就1.7.0和1.7.1中新增的功能做一个简单的汇总介绍。
HanLP 是由一系列模型与算法组成的 Java 工具包,目标是普及自然语言处理在生产环境中的应用。HanLP 具备功能完善、性能高效、架构清晰、语料时新、可自定义的特点。 在提供丰富功能的同时,HanLP 内部模块坚持低耦合、模型坚持惰性加载、服务坚持静态提供、词典坚持明文发布,使用非常方便,同时自带一些语料处理工具,帮助用户训练自己的模型。
1.7.0 更新如下:
新增文本聚类模块(k-means和repeated bisection)
词法分析器新增流水线模式
词法分析器加入规则 enableRuleBasedSegment #991
支持通过 JVM 的启动参数指定 data 路径:java -DHANLP_ROOT=/opt/hanlp 则加载/opt/hanlp/data #983
分词断句支持指定断句颗粒 #1018
CustomDictionary.insert("新词语", "词性标签") 支持省略频次
NeuralNetworkDependencyParser 构造函数接受 Segment
TextRankKeyword 支持构造自任意分词器
优化双数组 trie 树,构建后自动 shrink 到最低内存 #984
修订简繁词典
微调 ngram 和 nr 模型
新数据包 data-for-1.7.0.zip MD5 = 4c396f3039230ddfcef20865264512b1
Portable 版同步升级到 v1.7.0
HanLP v1.7.1 更新内容:
新增可自定义用户词典的维特比分词器 @AnyListen
利用 BufferedOutputStream 加速缓存生成,快37倍
自定义词典兼容含有空格的路径,fix #1025
增加 isCustomNature 方法
使热更新产生的缓存文件包含用户词性,fix #1028
修复可变 DAT 的 entrySet 方法,fix #1038
微调 ngram,简繁等
新数据包 data-for-1.7.1.zip MD5 = 9b8faa7fc7fddb24e27da27bd404126d
Portable 版同步升级到 v1.7.1
人工智能
2019-03-22 09:22:06
「深度学习福利」大神带你进阶工程师,立即查看>>>
带你看数据挖掘与机器学习-厦大EDP上课出勤预测
标签: 数据挖掘 特征工程 机器学习 出勤预测
write by xmhexi 2019/3/22
内容提要
首先说明本文是一篇科普文章,通过一个实际案例,帮助理解什么是数据挖掘、特征工程、机器学习等,文章中并不涉及详细的技术与参数。
本人刚刚起步初学,文章中均为本人的理解,有不妥之处,敬请指出。
文章中涉及的的姓名,电话等信息均做了模糊处理。
案例说明
事情的开始很简单,我的EDP课程已经上了8次课了,在班级群里,老师发了一张上课出勤表,让大家核对。 出勤表大概长这个样子的:
看着这张出勤表,我在想我们能做些什么呢?
能不能知道下次上课,谁会来,谁不会来,或者只来半天?
正好开始学习数据分析,那就用这些数据来做一个“上课出勤预测模型”吧,通过这些数据来最终预测下一次上课的出勤情况。
预测目标:下节课所有同学的出勤情况。
数据准备
说干就干,首先要获取这些数据,把出勤表图片经过剪切、识别:
最终得到对应的数据:
同学们有没来上课,跟什么会有关系呢?
寻找“有没来上课”有关系的东西,并把这些东西变成数字,就叫做“特征工程”了。
接下来就来寻找这些特征了。
特征工程
手上还有的资料就是同学录了,就从同学录入手。
脱敏处理
有些数据是比较敏感的,不适合到处传播,比如姓名,手机号,公司名称等,需要先进行一些处理,让这些数据可以使用但又不会被猜出来。
比如姓名就换成拼音的首字母,手机就保留前7位,公司名称就用序号来表示,其它的就可以删除了。
班委
都说“有担当 重责任”,担任班委职务是不是跟出勤有点关系?
班委信息按级别,小组长是1,班长是4; 责任越大应该越会来上课吧?
性别
都说男女有别,在决定去不去上课的时候,应该也是有差别的吧。
性别变成数字,女=0 男=1;
距离
在哪里上班(公司地点);离学校远不远? 外地的同学来上课肯定是阻力重重啊。
这个特征可以从公司名称查到公司地址,为了省事,我就用手机查询到手机号所的省、市。
先导入手机归属地数据,然后查看下同学们的手机所在地的情况:

根据手机所在的省和市,把同学们离学校的距离分成了4种类型: 本地的,周边的地区的,本省的;外省的;
职务
同学在公司的职务级别会不会影响来上课? 越是领导越忙,肯定有关系呢。
看一下有哪些职位:
职位太杂了,做下归类:
可以看出,大多同学都是经理和总监级别。
最后归成几类数字越大,级别越高:
结伴同行
都说“青春做伴好还乡”,同一个公司如果同时有几个伙伴一起来上课,那来上课的干劲应该高一点。
先看看各个公司派出伙伴的情况:
可以看出大多公司都是1个小伙伴,最高的有10个小伙伴。
最后按照派出小伙伴的数量高低分成5个级别:
星座
都说处女座的做事特别不一样,脑洞大开一下,如果找到同学的星座信息,这也可能成为影响来不来上课的因素!
找到班级同学过生日的信息:
对应计算出同学属于哪个星座,变成一个数字:
天气
天气会不会影响来上课的决定? 有可能噢。
上网找出了所有上课日期对应上课地点的天气情况。
不过这个特征没有加进去,留做以后再处理吧。
合成数据
找了这么多东西,最终得到了一些数据,把每个同学每节课的出勤情况做为一条记录,得到了1104条数据。
数据模型
上面找了这么多影响上课的因素(特征),到底哪些因素有关系,怎么样的关系呢,这个不用我们操心,建立一个数据模型就可以自动计算了。 我用了一个最简单的“决策树模型”,并进行“训练”,其实就是得到这些参数对结果的影响。
训练完成后,对已知的数据进行测试验证,看下准确度有多少:
哈哈,还不错哦,准确率有80%,也就是说用这个模型去预测未来,有8成的准确率。
那就用这个模型来计算下所有同学下一次上课的出勤情况,看下谁来谁不来,这个就叫“预测”:
预测完了是一堆的数据,为了好看,要处理成对应的格式:
怎么看数据呢?这张图中最后面的表格:
name_py就是同学的姓名拼音首字母;
"C9-1","C9-2"分别是下次上课的两天课程;
“1.0"表示这位同学来上课;
“0.0”表示这位同学不来上课;
“0.5"表示这位同学来上半天后就走了(嘻);
完整的预测结果
为了方便同学位查一下预测结果里自己来不来,我把所有的预测结果放在下面,重申一下:结果是预测出来的,准确度有限,权当游戏一把,别当真! name_py,C9-1,C9-2 CXF,1,1 CDL,0,0 CMS,1,1 CQL,0,0 CYF,0,0 CZY,1,1 DX,1,1 FCX,1,1 FJT,0,0 FRK,0,0 FZY,1,1 FPP,1,1 GQH,1,1 GBS,1,1 HLM,0,0 HSJ,0,0 HX,1,1 HQJ,1,1 HHD,1,1 HHS,0,0 HTY,0,0 HYL,1,1 KZT,1,1 KYP,1,1 LFL,0,0 LWJ,1,1 LXH,0,0 LBC,1,1 LJG,1,1 LLH,1,1 LPH,0,0 LSJ,1,1 LYF,1,1 LBR,1,1 LGQ,0,0 LKC,1,1 LDF,1,1 LSB,1,1 LSF,0,0 PDZ,1,1 PSZ,0,0 QWH,1,1 QXH,1,1 SGZ,0,0 SYP,1,1 WJP,1,1 WXM_188,0,0 WYJ,1,1 WZ,1,1 WQL,1,1 WXM_155,1,1 XGC,1,1 XYH,1,1 YXB,1,1 YCP,1,1 YQX,0,0 ZH,1,1 ZJQ,0,0 ZSC,1,1 ZXY,1,1 ZY,1,1 ZWJ,0,0 ZLF,0,0 ZWF,1,1 ZYQ,1,1 ZQH,0,0 ZJW,1,1 ZYN,0,0 ZLJ,1,1
特别注明:有两位同学的拼音都是WXM,所以用手机号前3位附在后面以示区别。
调整模型后重新更新了预测数据,最后更新时间:2019.4.1
结束语
当然,上面的数据中还可以分析出很多有价值的信息;还可以使用更为复杂的模型,来做出更加准确的预测。
从开始有想法到完成整个项目,花了很多时间和精力,毕竟是本人首个完成的实际案例。
在此过程中要感谢EDP老师、同学提供了这么好的素材;
感谢QQ群里为我提供帮助的小伙伴们;
还要感谢我的家人和同事,提供了让我完成的时间与环境,谢谢你们!
后记
关于案例还是有些内容想要补充一下:
模型优化
上面文章中提到,这个模型的准确度只有80%,好象不太准。 还需要对模型进行优化,有几个方向: 对模型的参数进行调整,也就是常说的“调参” 再挖掘数据中更多的特征点;
本文中可以挖掘的特征点还很多,比如:
“上课时间是在一个月的什么时间,上旬中旬还是下旬?”
“有没有连续不来上课的记录或者是请假的记录?”
“对于外地游学这个课程,重要程度比其它课程高。”
“同一次课程里,第二天来上课与第一天有点不同。”
“在公司是从事哪个部门,技术、人力还是销售?”
“所在的公司是哪个行业,生产型还是互联网?” 使用不同的模型来做训练,并做对比; 扩充数据,看看其它班级还有数据加进来,让数据更丰富;
视野
“当你知道的越多,未知的也会越多。”
分析完数据后,能带给我们什么呢,除了预测结果以外,其实还可以分析训练完的模型。
模型中每个因素(特征)都有一个对应的系数,表示它对结果的影响程度,有的因素(特征)的系数比较大,属于关键因素(关键特征)。从模型里可以看到,哪个因素(特征)对结果的影响比较大。
这就为我们提供了控制结果的可能性,只要控制好这些核心因素,就能对最终的结果产生想要的影响。例如: 在上面案例中,如果我们发现“离学校的距离”,“公司职位的高低”是影响“出勤率”的关键因素,那么在招生时进行优化,老师也可以对这些学生给予特别的关注。 如果在“用户下单预测模型”中发现,“有没有咨询过客服”是“最终下单”这个结果的关键因素之一,那么就可以通过“主动发起”等动作来促进用户下单。
人工智能
2019-03-22 02:34:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
https://36kr.com/p/5170370.html
人工智能
2019-03-21 23:54:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
人工智能时代已经来临,文明将驶向何方,人类会变成什么样?
2014年霍金接受BBC采访时说:“人工智能的全面发展将宣告人类的灭亡。”即使面对霍金的警告,人工智能时代却已经到来。而关于人工智能对未来人类社会的发展将带来的影响,人类似乎从没有达成一致的意见。
在电影纪录片《你相信电脑吗?》中,火星殖民计划的开创者,创新技术的狂热者埃隆·马斯克却对AI技术无比的担心,他说:“所有的人类独裁者都固有一死,但数字化的超级智能有朝一日会变成一个不朽的独裁者,到时候我们谁也逃不了。”
但是同样出演这部电影的奇点大学校长雷·库兹韦尔,与他表现出了截然不同的态度。他预测,2029年将会出现与人相媲美的人工智能,到了2045年人类将得到永生。
两位大佬的猜想恰恰反映人类社会对未来AI人工智能的两大看法,也说明未来人类社会的中心思想就是高科技与低端生活,控制与被控制的人类。
第一种是人类与机器人共存,彻底改革社会体制,建立新的分配制度。机器人代替人类做大部分事物,更加有效率,越来越多的人不需要工作就能享受生活。另一种可能性就是大量人口失业,资源分配不合理,人类变成这个系统的负担,社会因此动荡不安,等等。
第一种对未来的幻想是很美好的,乐观到大同世界离人类似乎触手可及。第二个可能看起来是很糟糕,人类社会秩序发生混乱。
那么随着人工智能时代的到来,人类文明将驶向何方,人类会变成什么样?科学文明的进步让人类在好奇的同时又内心夹杂担忧,但是值得说的是一个庞大的人工智能时代正在向我们缓缓走来。
人工智能
2019-03-21 15:52:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
近年来,我们已经看到了大数据的成功应用,但根据 研究 ,只有20%的员工能够真正的使用BI工具。此外,由于在统计思维方面缺乏培训且图表和图表形式的数据不是很清晰,决策者往往会出现误解和决策失误。而这背后的一切其实就是人工智能技术的子集- 自然语言处理 ,自然语言理解和自然语言生成以及他们的分析算法。
早些时候,企业需要一定的人力和持续监控半智能机器来理解和遵循预先编程的算法。但随着时间的推移以及人工智能、机器学习、人工神经网络、深度学习、自然语言处理和自然语言生成的发展,机器变得足够智能,也能满足特定的业务需求和目标。
战略性地简化和利用这些基于AI的技术可以理解庞大的数据集,从而产生有价值的见解,最终有助于开发定制的、有影响力的解决方案。谷歌,苹果,微软和阿里巴巴等IT巨头都依赖此类算法来改进产品推荐、在线搜索、智能语音的移动服务等。
NLP vs. NLU vs. NLG
尽管人们可能会遇到令人生畏的技术术语,但NLP,NLG和NLU似乎是用于解释简单过程的复杂缩略词。
1、NLP是指计算机读取并将输入文本转换为结构化数据;
2、NLU意味着理解计算机捕获的文本/统计数据;
3、NLG是指计算机将结构化数据转换为文本并以人类语言编写信息;
自然语言处理的阅读部分很复杂,其中包括许多功能,例如:
1、不雅表达的语言过滤器;
2、涉及人类情感的 情感分析 ;
3、主题分类;
自然语言理解(NLU)是人工智能的重要子集,它出现在自然语言处理之后,用于真正理解文本的内容并提取隐藏在其中的含义。像Alexa,Siri,Google Assistant这样的会话AI机器人就是使用NLU和NLG来达到目的。
了解 NLG 的真正潜力
人类总是需要数据来制定新的想法并进行沟通。但是,随着需要评估的数据量变大以及显著降低成本的需求,企业需要确定一种新的简化方法,自然语言生成(NLG)就是目前最佳的方法之一。
自然语言生成(NLG)主要的好处在于它能够将数据集转换为人类理解的清晰叙述。在处理电子表格中存在的统计数据时,NLG可以生成丰富的信息,这与自然语言处理不同,后者仅评估文本以形成见解。
通过自然语言生成,可以准确地评估、分析和传达数据。通过常规分析和相关任务的智能自动化,可以提高生产力,这样人类就可以专注于更具创造性、高价值的回报活动。
在一个有趣的用例中,美联社利用自然语言生成成功的从公司收益报表中生成了报告。这意味着他们不再需要人类消耗他们的时间和精力去解决这些问题。更重要的是,NLG一旦被完美设置就会自动生成数以千计的报告。
自然 语 言生成的好 处 是什么?
自然语言生成的好处超出了人们对于采用人工智能的普遍看法。因为它对营销和业务管理也有很多好处:
自动化创建内容
NLG主要能够做的是根据NLP和NLU之前处理的信息,在有组织结构的数据上创建,通过将结构良好的数据放置在精心配置的模板中,NLG可以自动输出并提供可记录的数据形式,例如分析报告、产品描述、以数据为中心的博客文章等。在这种情况下,依靠算法编程的机器可以完成以内容开发者所希望的格式创建内容。他们唯一要做的就是通过流行的媒体渠道向目标受众宣传,因此,自然语言生成可以为内容开发者和营销人员提供两大利器:
1、内容生成自动化;
2、以预期格式传送数据;
内容生成可以围绕Web挖掘、搜索引擎API来开发依靠各种在线搜索的结果和参考文档创造有价值的内容。到目前为止,已经出现了几个基于NLG的文本报告生成系统,比如说以根据输入的天气数据生成文本天气预报报告。
人类参与的显著减少
随着自然语言生成系统不断的优化,雇用具有数据素养的专业人员并训练他们完成工作变得非常重要。通过使用自然语言生成,企业家会慢慢意识到雇用理解复杂数据的人员是多么昂贵,为了提升企业效益,注定会有人被淘汰。 Gartner预测 ,在未来20%的业务内容将通过使用自然语言生成的机器编写,其中法律文件、股东报告、新闻稿或案例研究将不再需要人类创建。
预测性库存管理
商店库存管理的成功都会在业务目标和总体利润方面产生巨大的推动。而在供应链管理中,获取并分析生产率、销售数据尤为重要。根据这些信息,商店经理可以了解如何将库存维持在最佳水平。然而,管理者并不可能完完全全的掌握到实时数据。
此时,更高级的NLG可以作为数据分析的交互式媒介,使整个数据报告流程无缝且富有洞察力。商店经理无需通过数据的多个图表和条形图,就可以获得所需格式的清晰叙述和分析。通过自然语言生成,管理者将拥有最佳预测模型,以此对商店绩效和库存管理提供明确的指导和建议。
如何应用自然语言生成?
对于希望采用并获得自然语言生成优势的企业而言,拥有某些要素时至关重要的,例如:
必须具有匹配的场景
并非每个内容创建场景都需要自然语言生成。NLG是一种独特的技术,旨在产生特定的答案,生成你在博客上看到的内容是不可能的。如果你定期传达的任务具有一致的格式,NLG可能是自动执行这些任务的最佳资源。
举个例子,一个著名的营销机构 PR 20/20 利用自然语言生成,在生成Google Analytics的分析报告时少用80%的时间。另一个例子是 华盛顿邮报 ,它们创建了Heliograf,它是一个基于人工智能的引擎,使用自然语言生成为2016年奥运会和选举竞赛撰写快讯。
树立现实的目标
人工智能技术需要一些时间才能实现自动化操作,要整合并收获自然语言生成的好处,需要一定的时间。你选择的智能是有价格标签的,因此你应该对你的精确要求,AI的实际功能和可扩展性保持理智。如果NLG在生成报告和叙述时真的减少了组织的成本,你可以选择它。
数据必须足够结构化
AI需要特定形式的输入,NLG只有在输入结构化数据时才会起作用。检查你的数据集是否已组织和优化。确保你上传的数据干净、一致且易于使用。
结论
聊天 机器人 将更加智能 ,不再是简单的查询以及提供简单的对话,而且未来高级NLG系统还将参与企业特定的工作流程管理,它们将帮助管理人员和员工在客户之间建立一个更优越的互动网络,以在最短的时间内实现业务动态并产生准确的输出。
最后,对于面临数据分析和多语言支持挑战的企业而言,可以利用自然语言生成的优势实现报告创建、内容生成的实时自动化。有了NLG,企业应该考虑整合能够以最终用户期望的格式有效地生成信息的会话界面,最终增加用户的参与度。
作者:【方向】
原文链接
本文为云栖社区原创内容,未经允许不得转载。
人工智能
2019-03-28 12:41:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
前言 只有光头才能变强。
回顾前面: 从零开始学TensorFlow【01-搭建环境、HelloWorld篇】 什么是TensorFlow? TensorFlow读写数据
不知道大家最开始接触到axis的时候是怎么样的,反正我是挺难理解的..我们可以发现TensorFlow的很多API都有axis这个参数,如果我们对axis不了解,压根不知道API是怎么搞的。 一句话总结axis:axis可以方便我们 将数据进行不同维度的处理 。
一、理解axis
如果你像我一样,发现API中有axis这个参数,但不知道是什么意思。可能就会搜搜axis到底代表的什么意思。于是可能会类似搜到下面的信息: 使用0值表示沿着每一列或行标签\索引值向下执行方法( axis=0代表往跨行 )
使用1值表示沿着每一行或者列标签模向执行对应的方法( axis=1代表跨列 )
但我们又知道,我们的数组不单单只有二维的,还有三维、四维等等。 一旦维数超过二维,就无法用简单的行和列来表示了 。
所以,可以用我下面的方式进行理解: axis=0 将最开外头的括号去除,看成一个整体,在这个整体上进行运算 axis=1 将第二个括号去除,看成一个整体,在这个整体上进行运算 ...依次类推
话不多说,下面以例子说明~
1.1二维数组之concat
首先,我们来看个 concat 的例子,concat第一个参数接收val,第二个参数接收的是axis def learn_concat(): # 二维数组 t1 = tf.constant([[1, 2, 3], [4, 5, 6]]) t2 = tf.constant([[7, 8, 9], [10, 11, 12]]) with tf.Session() as sess: # 二维数组针对 axis 为0 和 1 的情况 print(sess.run(tf.concat([t1, t2], 0))) print(sess.run(tf.concat([t1, t2], 1)))
ok,下面以 图示 的方式来说明。现在我们有两个数组,分别是t1和t2:
首先,我们先看 axis=0 的情况,也就是 tf.concat([t1, t2], 0) 。从上面的描述,我们知道,先把第一个括号去除,然后将其子内容看成一个整体,在这个整体下进行想对应的运算(这里我们就是concat)。
所以最终的结果是: [ [1 2 3], [4 5 6], [7 8 9], [10 11 12] ]
接着,我们再看 axis=1 的情况,也就是 tf.concat([t1, t2], 1) 。从上面的描述,我们知道,先把第二个括号去除,然后将其子内容看成一个整体,在这个整体下进行想对应的运算(这里我们就是concat)。
所以最终的结果是: [ [1, 2, 3, 7, 8, 9] [4, 5, 6, 10, 11, 12] ]
1.2三维数组之concat
接下来我们看一下三维的情况 def learn_concat(): # 三维数组 t3 = tf.constant([[[1, 2], [2, 3]], [[4, 4], [5, 3]]]) t4 = tf.constant([[[7, 4], [8, 4]], [[2, 10], [15, 11]]]) with tf.Session() as sess: # 三维数组针对 axis 为0 和 1 和 -1 的情况 print(sess.run(tf.concat([t3, t4], 0))) print(sess.run(tf.concat([t3, t4], 1))) print(sess.run(tf.concat([t3, t4], -1)))
ok,下面也以 图示 的方式来说明。现在我们有两个数组,分别是t3和t4:
首先,我们先看 axis=0 的情况,也就是 tf.concat([t3, t4], 0) 。从上面的描述,我们知道,先把第一个括号去除,然后将其子内容看成一个整体,在这个整体下进行想对应的运算(这里我们就是concat)。
所以最终的结果是: [ [ [1 2] [2 3] ] [ [4 4] [5 3] ] [ [7 4] [8 4] ] [ [2 10] [15 11] ] ]
接着,我们再看 axis=1 的情况,也就是 tf.concat([t3, t4], 1) 。从上面的描述,我们知道,先把第二个括号去除,然后将其子内容看成一个整体,在这个整体下进行想对应的运算(这里我们就是concat)。
所以最终的结果是: [ [ [1 2] [2 3] [7 4] [8 4] ] [ [4 4] [5 3] [2 10] [15 11] ] ]
最后,我们来看一下 axis=-1 这种情况,在文档也有相关的介绍: As in Python, the axis could also be negative numbers. Negative axis are interpreted as counting from the end of the rank, i.e., axis + rank(values) -th dimension
所以,对于我们三维的数组而言,那 axis=-1 实际上就是 axis=2 ,下面我们再来看一下这种情况:
最终的结果是: [ [ [1 2 7 4] [2 3 8 4] ] [ [4 4 2 10] [5 3 15 11] ] ]
除了concat以外,其实很多函数都用到了axis这个参数,再举个例子: >>> item = np.array([[1,4,8],[2,3,5],[2,5,1],[1,10,7]]) >>> item array([[1, 4, 8], [2, 3, 5], [2, 5, 1], [1, 10, 7]]) >>> item.sum(axis = 1) array([13, 10, 8, 18]) >>> item.sum(axis = 0) array([ 6, 22, 21])
参考资料: 有关axis/axes的理解 https://zhuanlan.zhihu.com/p/25761406
最后
下一篇是TensorBoard~ 乐于输出 干货 的Java技术公众号:Java3y。公众号内有200多篇 原创 技术文章、海量视频资源、精美脑图,不妨来 关注 一下!
觉得我的文章写得不错,不妨点一下 赞 !
人工智能
2019-03-27 18:57:00
「深度学习福利」大神带你进阶工程师,立即查看>>>
在ubuntu下编译tensorflow的android库的步骤: 安装android sdk 安装android ndk 安装 能够编译tensorflow的对应版本的bazel(我也不知道具体的对应关系,早先tensorflow版本无提示,后来版本的tensorflow有不提示),我使用的是 tensorflow-1.8.0和bazel-0.10.0 下载需要编译的版本的tensorflow源代码,或者git clone ,随意 安装python,还有protobuf等库类,这个可以在具体的编译过程查漏补缺,也可以按照官网的依赖直接安装完 到tensorflow的目录下, ./configure,配置相关的变量,选项,如 android SDK、NDK等 编译so文件,在目录bazel-bin/tensorflow/contrib/android 找到编译的 libtensorflow_inference.so 拷贝到需要防止的位置,继续进行下一步的操作,不拷贝的话,后面的编译会把前面的覆盖。 bazel build -c opt //tensorflow/contrib/android:libtensorflow_inference.so \ --crosstool_top=//external:android/crosstool \ --host_crosstool_top=@bazel_tools//tools/cpp:toolchain \ --cpu=arm64-v8a # 根据需要修改对应的参数,主要就是target 板子的CPU架构 编译jar包,在目录 bazel-bin/tensorflow/contrib/android 下找到 libandroid_tensorflow_inference_java.jar,拷贝到具体的位置备用。 bazel build //tensorflow/contrib/android:android_tensorflow_inference_java 编译 android_demo bazel build -c opt --config=arm-v8a //tensorflow/examples/android:tensorflow_demo
人工智能
2019-03-27 17:58:00