文章Flyai人工智能社区参赛非官方指南—代码篇
Flyai人工智能社区参赛非官方指南—代码篇春节前写了个简易版的参赛指南,居然得到了11个赞,通过对比,发现这已经算是多的了,所以感谢这11位小伙伴不吝鼠标左键。通过这些天观察,发现有不少新的小伙伴,想着这些小伙伴将是祖国的未来,顿时感觉应该发挥一下余热,让新来的小伙伴少走弯路,尽快上手FLYAI赛事,领略AI魅力,得个好名次,赚点零花钱,实现小目标,迎娶白富美,走上人生巅峰……好了,废话不多说,针对一些小伙伴提出的没有baseline的问题,这次是代码篇,以实例说话,主要介绍main.py和prediction.py这两个文件框架。因为这两个文件最重要,其它的文件最终都是为这两个文件服务的。理论上我们只要完成这两个文件的编写,就可以完成比赛了。下面,我将以“1024程序员节—蝴蝶分类开源竞赛”为例,讲解一下这两个文件的结构和编写方法。因为这个比赛开源了,所以小伙伴们可以免费下载这个比赛的代码。 一、main.py将官方样例下载到本地,解压后,就可以看到这个main.py文件,打开它,便得到下面的代码框架: # -*- coding: utf-8 -*- import argparse import os from flyai.data_helper import DataHelper from flyai.framework import FlyAI from path import MODEL_PATH ''' 此项目为FlyAI2.0新版本框架,数据读取,评估方式与之前不同 2.0框架不再限制数据如何读取 样例代码仅供参考学习,可以自己修改实现逻辑。 模版项目下载支持 PyTorch、Tensorflow、Keras、MXNET、scikit-learn等机器学习框架 第一次使用请看项目中的:FlyAI2.0竞赛框架使用说明.html 使用FlyAI提供的预训练模型可查看:https://www.flyai.com/models 学习资料可查看文档中心:https://doc.flyai.com/ 常见问题:https://doc.flyai.com/question.html 遇到问题不要着急,添加小姐姐微信,扫描项目里面的:FlyAI小助手二维码-小姐姐在线解答您的问题.png ''' if not os.path.exists(MODEL_PATH): os.makedirs(MODEL_PATH) # 项目的超参,不使用可以删除 parser = argparse.ArgumentParser() parser.add_argument("-e", "--EPOCHS", default=10, type=int, help="train epochs") parser.add_argument("-b", "--BATCH", default=32, type=int, help="batch size") args = parser.parse_args() class Main(FlyAI): ''' 项目中必须继承FlyAI类,否则线上运行会报错。 ''' def download_data(self): # 根据数据ID下载训练数据 data_helper = DataHelper() data_helper.download_from_ids("ButterflyClassification") def deal_with_data(self): ''' 处理数据,没有可不写。 :return: ''' pass def train(self): ''' 训练模型,必须实现此方法 :return: ''' pass if __name__ == '__main__': main = Main() main.download_data() main.train() 前面导入几个库,我们不用管它,且看“项目的超参,不使用可以删除”这行注释下面这几行代码,其作用就是接收我们训练设置对话框里的BATCH和EPOCH的参数,如果没有用到,就不用管它,删掉也没事。在代码的下半部分,我们可以看到有一个叫Main的类,这里只有一个框架,我们得把这个类写完整。样例里,Main类有三个功能:download_data,deal_with_data,train。如有需要,我们也可以添加其它功能,其实我们只要完成这三个功能就够了,下面分别阐述。 1、download_data功能函数它的作用就是从服务器上面下载数据,它将在当前目录建立一个data文件夹,下载的训练数据就存在这个文件夹里,如果是本地调试下载,只能下载部分数据,线上训练时才能下载全部数据。图片数据存在data/input/ButterflyClassification/image目录下,data/input/ButterflyClassification/目录下有个train.csv文件,这个文件很重要,是图片数据的标签文件,里面是对应图片的分类信息,我们打开这个文件看一下:第一栏,image_path栏下是图片路径信息,需要注意的是路径不是完整的,我们在代码中读取后,还需要补全路径。第二栏,label栏是图片标签信息。这就是download_data实现的功能,我们不用管它,只需执行就可以了,官方已经把它实现好了。 2、deal_with_data功能函数该功能主要实现数据的读取和处理,得根据自身的需要来编写。先说读取,读取一般分两个步骤,先用pandas库读取train.csv文件内容,当然你可以用其它库。使用前别忘import pandas as pd, 代码如下: df = pd.read_csv(os.path.join(DATA_PATH, 'ButterflyClassification', 'train.csv')) image_path_list = df['image_path'].values label_list = df['label'].values image_path_list是路径信息列表,如[“image/5697.jpg”, “image/17805.jpg”,……],label_list是标签列表,如[“Danaus_chrysippus”, “Losaria_coon”,……],这两个列表长度一样,图片路径和标签是一一对应关系。如果要改动其中一个列表,那么另一个列表也要对应改动,才能保持一致。得到train.csv的信息后,我们进入第二个步骤,读取图片和建立标签,这里可以用cv2或者PIL读取图片,用对应的数字建立标签,这个比较简单。再说处理,图片数据处理略为复杂,不过主要有放大、缩小、剪裁、归一化、训练集和验证集划分等,一般来说,还要进行一些数据增强处理,比如亮度、对比度变化,随机剪裁、随机擦除、平移、翻转等,keras中有ImageDataGenerator,pytorch中有transforms,这里不过多述叙。总之数据的处理很重要,可以让模型训练得更加“鲁棒”,提高分数。数据处理完成后,就可以构建自己的模型,在图片分类中,我们一般都采用预训练模型,这样不用从头训练,也不需要庞大的数据训练,很快就能得一个不错的结果。我们可以在Main类之外,编写模型代码,也单独可以建立一个模型文件,如net.py,然后调用即可。 3、train功能函数顾名思义,该函数主要是完成模型的训练。在这里,我们可以定义模型的优化器、学习率、损失函数、训练次数等,最后将训练好的模型保存到MODEL_PATH路径下,MODEL_PATH的定义可以查看path.py文件。 下面以keras为例实现一个简易版的main.py: # -*- coding: utf-8 -*- import argparse import os import cv2 from flyai.data_helper import DataHelper from flyai.framework import FlyAI from path import MODEL_PATH from net import mymodel from keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau from tensorflow.compat.v1 import ConfigProto from tensorflow.compat.v1 import InteractiveSession # tensorflow2.1 keras2.3.1需要加入下面三行,否则在服务器上运行会报错 config = ConfigProto() config.gpu_options.allow_growth = True session = InteractiveSession(config=config) ''' 此项目为FlyAI2.0新版本框架,数据读取,评估方式与之前不同 2.0框架不再限制数据如何读取 样例代码仅供参考学习,可以自己修改实现逻辑。 模版项目下载支持 PyTorch、Tensorflow、Keras、MXNET、scikit-learn等机器学习框架 第一次使用请看项目中的:FlyAI2.0竞赛框架使用说明.html 使用FlyAI提供的预训练模型可查看:https://www.flyai.com/models 学习资料可查看文档中心:https://doc.flyai.com/ 常见问题:https://doc.flyai.com/question.html 遇到问题不要着急,添加小姐姐微信,扫描项目里面的:FlyAI小助手二维码-小姐姐在线解答您的问题.png ''' if not os.path.exists(MODEL_PATH): os.makedirs(MODEL_PATH) # 项目的超参,不使用可以删除 parser = argparse.ArgumentParser() parser.add_argument("-e", "--EPOCHS", default=10, type=int, help="train epochs") parser.add_argument("-b", "--BATCH", default=32, type=int, help="batch size") args = parser.parse_args() class_dict = {'Danaus_chrysippus': 0, 'Losaria_coon': 1,......} # 这里略写了,需要补充 class Main(FlyAI): ''' 项目中必须继承FlyAI类,否则线上运行会报错。 ''' def download_data(self): # 根据数据ID下载训练数据 data_helper = DataHelper() data_helper.download_from_ids("ButterflyClassification") def deal_with_data(self): ''' 处理数据,没有可不写。 :return: ''' df = pd.read_csv(os.path.join(DATA_PATH, 'ButterflyClassification', 'train.csv')) image_path_list = df['image_path'].values label_list = df['label'].values x_data = [] y_data = [] for image_path, label in zip(image_path_list, label_list): image_path = os.path.join(DATA_PATH, 'ButterflyClassification', image_path) image = cv2.imread(image_path) image = cv2.resize(image, (224, 224)) x_data.append(image) y_data.append(class_dict[label]) self.x_data = np.array(x_data) / 255. self.y_data = np.array(y_data) def train(self): ''' 训练模型,必须实现此方法 :return: ''' model = mymodel() # 这个model需要自己编写 model.compile(loss='sparse_categorical_crossentropy', optimizer='adam', metrics=['acc']) model_path = os.path.join(MODEL_PATH, 'model.h5') mp = ModelCheckpoint(filepath=model_path, save_best_only=True, save_weights_only=False, mode='min', monitor='val_loss', verbose=1) reduce_lr = ReduceLROnPlateau(monitor='val_loss', mode='auto', factor=0.1, patience=3, verbose=1) el = EarlyStopping(monitor='val_loss', patience=5, verbose=1, mode='min') cllist = [mp, reduce_lr, el] batch_size = 16 his = finalmodel.fit(self.x_data, self.y_data, batch_size=batch_size, verbose=2, epochs=20, validation_split=0.1, callbacks=cllist, ) if __name__ == '__main__': main = Main() main.download_data() main.train() 可以看出,在main.py文件里,我们主要完成deal_with_data和train功能函数的编写,还有模型的建立,有数据有模型就要以进行训练了。 二、prediction.py在评论里看到,有的小伙伴不懂得prediction.py的编写,这个可是个收获果实的文件,训练半天,保存了模型,就需要这个文件来调用,在测试集上进行验证,得到模型分数。好吧,我们先来看文件结构: # -*- coding: utf-8 -* from flyai.framework import FlyAI class Prediction(FlyAI): def load_model(self): ''' 模型初始化,必须在此方法中加载模型 ''' pass def predict(self, image_path): ''' 模型预测返回结果 :param input: 评估传入样例 {"image_path": "./data/input/image/172691.jpg"} :return: 模型预测成功中,返回预测结果格式 {"label": "Losaria_coon"} ''' # 假设图片image_path对应的标签为Losaria_coon, 则返回格式为 {"label": "Losaria_coon"} return {"label": "Losaria_coon"} 该文件中只有一个类Prediction,该类有两个功能,一个是load_model,另一个是predict。 1、load_model该功能主要实现模型载入,就是将前面训练好,保存在MODEL_PATH路径下面的模型文件载入。以keras为例,如果前面保存在MODEL_PATH路径下面的模型名称为model.h5,那么在load_model功能中我们可以这样写: model_path = os.path.join(MODEL_PATH, 'model.h5') self.model = load_model(model_path) 当然,别忘了在文件头添加相应的import。 import os from path import MODEL_PATH from keras.models import load_model 这样就实现了模型的载入,至于keras和pytorch模型载入方法,可以查找一下相关资料,挺简单的! 2、predict很明显,这里就是实现模型的预测功能,它有一个参数image_path,这个是需要预测的图片路径,直接读取就行, 如: image = cv2.imread(image_path) 再用main.py文件中的方法调整图片的大小和归一化。 下面简要以keras为例,写一个简易版的prediction.py文件: # -*- coding: utf-8 -* import os import numpy as np import cv2 from flyai.framework import FlyAI from keras.models import load_model from path import MODEL_PATH, DATA_PATH from tensorflow.compat.v1 import ConfigProto from tensorflow.compat.v1 import InteractiveSession # tensorflow2.1 keras2.3.1需要加入下面三行,否则在服务器上运行会报错 config = ConfigProto() config.gpu_options.allow_growth = True session = InteractiveSession(config=config) class_num = ['Danaus_chrysippus', 'Losaria_coon',......] class Prediction(FlyAI): def load_model(self): ''' 模型初始化,必须在此方法中加载模型 ''' model_path = os.path.join(MODEL_PATH, 'model.h5') self.model = load_model(model_path) def predict(self, image_path): ''' 模型预测返回结果 :param input: 评估传入样例 {"image_path": "./data/input/image/172691.jpg"} :return: 模型预测成功中,返回预测结果格式 {"label": "Losaria_coon"} # 假设图片image_path对应的标签为Losaria_coon, 则返回格式为 {"label": "Losaria_coon"} ''' image = cv2.imread(image_path) image = cv2.resize(image, (224, 224)) image = np.array([image]) / 255. preds = self.model.predict(image)[0] pred = np.argmax(preds) return {"label": class_num[int(pred)]} 这样一个简易版的就完成了,小伙伴你学废了么? 总之,就是要多写多练,不要怕出错,有什么不明白,你就去撩小助手,小助手可是个单身的、漂亮的妹纸哦! pass:由于时间仓促,如有错漏,概不负责!!!

5

图像分类

天涯·明月·刀·2021-05-10 10:42 7 阅读 1.2k
文章Flyai人工智能社区参赛指南—非官方版
Flyai人工智能社区参赛指南—非官方版        牛年新春马上到来,在这里,先祝小伙伴们新春大吉,工作顺利,合家幸福!再祝小助手,天天美美哒!         不知不觉,在社区混迹有两年余,通过打怪升级,从一个AI新手成功的晋升为AI菜鸟!别人是天才,我是“抄添裁”,代码靠抄,适度增添,合理剪裁!ctrl+C(V)法力无边,千秋万代,一统江湖!为了给新手们提供点方便,让他们尽快上手,于是就想写个参赛指南,尽一个菜鸟应尽的责任和义务(主要还是因为新手看完官方的指南,往往找不着北)。下面,是依据flyai2.0 windows版本,结合自己的经验编写,不足之处,大家多多指正。          一、主页面         在浏览器中,输入www.flyai.com,进入官方网站主页面:         主页面上方有赛事、学习圈、实验室,以及自己的用户名。         当前主页面就是赛事页面,可以看到当前的比赛,你可以参加自己感兴趣的比赛。学习圈发布的主要是小助手不知道从哪里抄来的文章、其他小伙伴的水文和答辩经历等。实验室,是个什么东东,估计和google colab类似的东西吧,我也想进去看看。         参加比赛活动,需要个人注册,很简单,微信一扫基本OK了,然后官方会送1000的FlyAI分值,这些分值比赛会用到,基本是线上训练一分钟,就消耗1个FlyAI分值。曾经就有1000分在我面前,我没珍惜,直到用光,我才后悔莫急……现在主要靠每日签到赚个二三十分,痛并快乐着!         二、项目页面         注册好后,现在我们选取一个比赛来参加吧,比如上图第一个,心率不齐病症检测赛。         页面上,我们可以看到赛事介绍,竞赛排行榜,讨论等内容。我们通过阅读赛事介绍,可以了解比赛的相关内容,竞赛排行榜可以看到小伙伴们的比赛成绩,你还可以在讨论页面发表高论,赚个20FlyAI分。         好吧,我们现在要做的就是报名参赛,点击右上方那个金光灿灿的“报名参赛“按钮,然后,报名参赛就变成了查看样例,再点击”查看样例“,进入代码页面。         左侧边栏,是代码的文件,这里有五个,分别是app.json,main.py,path.py,prediction.py和requirements.txt。        app.json:这个是该项目的配置文件,不用管它。         main.py:这是代码的主要文件,将代码上传到服务器后,服务器就会自动执行该文件的代码,这个也可以说是代码的入口文件,我们将在这个文件中实现程序的主要功能,比如说建立模型、处理数据、训练模型、保存模型等。         path.py:这里面是路径的配置,默认有DATA_PATH和MODEL_PATH,你可以添加自己的路径配置。         prediction.py:main.py执行完毕后,服务器将调用该文件的Prediction类,依次调用这个类的load_model和predict功能,完成模型的验证,根据模型提交的结果,给出得分。         requirements.txt:这里面是该项目用到的包,比如你代码里面用的pytorch版本是1.4,那么,你可以在这个文件里面加入torch==1.4.0,官方服务器一般会把常用的包安装好了,比如说numpy、pandas等,一些不常用的,而你的代码里需要用到的,就可以在这个文件里面添加。         理论上,我们只要按照代码框架,完成main.py和prediction.py这两个文件代码的编写就行了。         右侧栏,就是相应文件的内容,我们可以在电脑上进行更改,完成后,点击下方的提交训练,就可以上传代码到服务器端进行训练。         右侧栏上方最右边,有三个按钮,分别是下载代码、上传代码、查看代码。下载代码,即可将代码下载到本地编辑,完成后,再打包,点击上传代码,即可将线下代码上传到线上;点击查看代码,可以查看以前提交过的代码,方便修改。         三、下载代码         线上编写,有很多不方便,我们可以下载下来线下编写调试。点击下载代码,解压后,可以得到如下文件:         可以看到,会比线上多出几个文件,多出的文件不用管它,我们只看这个flyai.exe,双击运行它,如下图:         在这里,可以点击“下载数据“按钮,下载部分数据到线下,这时会在当前文件下建立一个data文件夹,用来保存下载数据。点击”提交GPU训练“按钮,即可以将当前目录下的相关文件上传到服务器端进行训练。         我们先来看看下载下来的样例数据,样例数据只是全部数据的一部分,主要是用来线下调试代码的。比如,在路径“data/input/ArrhythmiaClassification/“下,有个train.csv文件,打开看看:         其中,data就是模型训练需要用到的数据,label是标签。在图像类的比赛,第一栏一般是图片的路径,不同的比赛,略有不同。         现在,就可以按照main.py和prediction.py里面样例框架,编写自己的代码,值得注意的是,训练好的模型文件要保存在“data/output/model”路径下面。根据需要,也可以另外新建文件夹或者文件。完成代码编写后,可先用线下数据,本地运行一下,看有没有什么BUG,确定无误后,就可以上传代码,到线上训练了。         四、上传代码         上传代码有两种方式,一种是直接点击上面flyai.exe图中的“提交GPU训练”,程序就会自动将当前文件夹下面的文件上传到线上,这里需要注意的是,有些我们自己建立的文件夹可能不会上传,所以,如果有新建的文件夹,建议不要采取这种方式上传代码。         另外一种上传方式是手动打包上传,这里要将app.json、main.py、prediction.py、path.py、requirements.txt这五个文件,加上我们新建的文件和文件夹,一起打包成压缩包,然后点击下图中“下载代码”旁边的上传按钮,就可以把我们的文件上传到线上了,再点击“提交训练”按钮,就可以训练了。        五、线上运行         这时候,我们可以查看程序运行状态,如下图:         由于线上服务器数量有限,可能需要排队,只能耐心等待,如果不想等,可以点击“取消排队”,但是不能重复提交代码训练,一次只能训练一个任务,如果提交两个以上任务,就会出现上图中的“已加入队列!之前任务完成后,将自动开始训练”,不过有时候,网站后台代码有问题,也会出现这种情况,这时候,就需要我们美丽的Flyai小助手出场了,将你遇见的问题,跟她反馈,一般都能很好解决。         训练日志内容会显示requirement.txt里面安装包的安装状况,之后就会显示执行main.py输出显示的内容,比如print。如果程序有错误,会显示错误提示,停止训练。         每训练一次,都会消耗我们的FLYAI分值,如果分值为零,就不能再进行训练了。获取积分的方式有每日签到、每日讨论、写写文章,邀请朋友和充钱等等,也可以坚持签到,像我一样发发文章,Fai值自然而来!         好了,基本上,常用的东西就是这样了,不过,各位一定确定以及肯定还会碰到各种问题,可以问小助手,边打怪边升级,钢铁就是这样练废的!

13

数据分析

天涯·明月·刀·2021-02-22 17:04 7 阅读 1.9k
文章距离度量方法
距离度量方法欧氏距离最常见的两点之间或多点之间的距离表示法,又称之为欧几里得度量,它定义于欧几里得空间中,如点 $x = (x_1,\ldots,x_n)$ 和 $y = (y_1,\ldots,y_n)$ 之间的距离为: d(x,y)=\sqrt{(x1-y_1)^2+(x_2-y_2)^2+…+(x_n-y_n)^2}=\sqrt{\sum{i=1}^{n}(x_i-y_i)^2} 二维平面上两点$a(x1,y_1)$与$b(x_2,y_2)$间的欧氏距离:d=\sqrt{\left(x{1}-x{2}\right)^{2}+\left(y{1}-y{2}\right)^{2}}三维空间两点$a(x_1,y_1,z_1)$与$b(x_2,y_2,z_2)$间的欧氏距离:d=\sqrt{\left(x{1}-x{2}\right)^{2}+\left(y{1}-y{2}\right)^{2}+\left(z{1}-z{2}\right)^{2}}两个n维向量$a(x{11},x{12},\ldots,x{1n})$与 $b(x{21},x{22},\ldots,x{2n})$间的欧氏距离:d=\sqrt{\sum{k=1}^{n}\left(x{1 k}-x{2 k}\right)^{2}}也可以用表示成向量运算的形式: d=\sqrt{(a-b)(a-b)^{T}} 曼哈顿距离定义曼哈顿距离的正式意义为L1-距离或城市区块距离,也就是在欧几里得空间的固定直角坐标系上两点所形成的线段对轴产生的投影的距离总和。例如在平面上,坐标$(x_1, y_1)$的点$P_1$与坐标$(x_2, y_2)$的点$P_2$的曼哈顿距离为:$|x_1-x_2|+|y_1-y_2|$,曼哈顿距离依赖座标系统的转度,而非系统在座标轴上的平移或映射。 通俗来讲,想象你在曼哈顿要从一个十字路口开车到另外一个十字路口,驾驶距离是两点间的直线距离吗?显然不是,除非你能穿越大楼。而实际驾驶距离就是这个“曼哈顿距离”,此即曼哈顿距离名称的来源, 同时,曼哈顿距离也称为城市街区距离。 二维平面两点$a(x1,y_1)$与$b(x_2,y_2)$间的曼哈顿距离 :d=|x_1-x_2|+|y_1-y_2|两个$n$维向量$a(x{11},x{12},\ldots,x{1n})$与 $b(x{21},x{22},\ldots,x_{2n})$间的曼哈顿距离: d=\sum{k=1}^{n}\left|x{1 k}-x_{2 k}\right| 切比雪夫距离若二个向量或二个点$p$和$q$,其座标分别为$pi$及$q_i$,则两者之间的切比雪夫距离定义如下:d = max{xi}(|x_2-x_1|,|y_1-y_1|)这也等于以下Lp度量的极值:$\lim{x\rightarrow\infty}(\sum_{i=1}^n|p_i-q_i|^k)^{\frac{1}{k}}$ ,因此切比雪夫距离也称为L∞度量。 以数学的观点来看,切比雪夫距离是由一致范数(或称为上确界范数)所衍生的度量,也是超凸度量的一种。 在平面几何中,若二点p及q的直角坐标系坐标为$(x1,y_1)$及$(x_2,y_2)$,则切比雪夫距离为:D{\text {Chess}}=\max \left(\left|x{2}-x{1}\right|,\left|y{2}-y{1}\right|\right) 玩过国际象棋的朋友或许知道,国王走一步能够移动到相邻的8个方格中的任意一个。那么国王从格子(x1,y1)走到格子(x2,y2)最少需要多少步?。你会发现最少步数总是max( | x2-x1 | , | y2-y1 | ) 步 。有一种类似的一种距离度量方法叫切比雪夫距离。 二维平面两点$a(x1,y_1)$与$b(x_2,y_2)$间的切比雪夫距离 :d = max{xi}(|x_2-x_1|,|y_1-y_1|)两个n维向量$a(x{11},x{12},\ldots,x{1n})$与$b(x{21},x{22},\ldots,x{2n})$间的切比雪夫距离:d=\max {i}\left(\left|x{1 i}-x{2 i}\right|\right)这个公式的另一种等价形式是 d=lim{k\to\infty}(\sum{i=1}^{n}|x{1i}-x{2i}|^k)^{1/k} 闵可夫斯基距离闵氏距离不是一种距离,而是一组距离的定义。两个n维变量$a(x{11},x{12},\ldots,x{1n})$与 $b(x{21},x{22},\ldots,x{2n})$ 间的闵可夫斯基距离定义为:d=\sqrt[p]{\sum{k=1}^{n}|x{1k}-x_{2k}|^p}其中p是一个变参数。当p=1时,就是曼哈顿距离当p=2时,就是欧氏距离当p→∞时,就是切比雪夫距离。根据变参数的不同,闵氏距离可以表示一类的距离。 标准化欧氏距离标准化欧氏距离是针对简单欧氏距离的缺点而作的一种改进方案。标准欧氏距离的思路:既然数据各维分量的分布不一样,那先将各个分量都“标准化”到均值、方差相等。至于均值和方差标准化到多少,先复习点统计学知识。 假设样本集X的数学期望或均值为m,标准差为s,那么X的“标准化变量”X表示为:(X-m)/s,而且标准化变量的数学期望为0,方差为1。即,样本集的标准化过程用公式描述就是:X^=\frac{X-m}{s}标准化后的值 = ( 标准化前的值 - 分量的均值 ) /分量的标准差  经过简单的推导就可以得到两个n维向量$a(x{11},x{12},\ldots,x{1n})$与 $b(x{21},x{22},\ldots,x{2n})$ 间的标准化欧氏距离的公式:d=\sqrt{\sum{k=1}^{n}(\frac{x{1k}-x_{2k}}{s_k})^2} 马氏距离有M个样本向量$X_1,\ldots,X_m$,协方差矩阵记为$S$,均值记为向量$\mu$,则其中样本向量$X$到$\mu$的马氏距离表示为:D(X)=\sqrt{(X-u)^TS^{-1}(X_i-X_j)}若协方差矩阵是单位矩阵(各个样本向量之间独立同分布),则公式就成了欧氏距离:D(X_i,X_j)=\sqrt{(X_i-X_j)^T(X_i-X_j)}若协方差矩阵是对角矩阵,公式变成了标准化欧氏距离。 马氏距离的优缺点:量纲无关,排除变量之间的相关性的干扰。 巴氏距离在统计中,巴氏距离距离测量两个离散或连续概率分布的相似性。它与衡量两个统计样品或种群之间的重叠量的巴氏距离系数密切相关。同时,Bhattacharyya系数可以被用来确定两个样本被认为相对接近的,它是用来测量中的类分类的可分离性。对于离散概率分布 p和q在同一域 X,它被定义为:DB(p,q)=-lnBC(p,q)其中:BC(p,q)=\sum{x\in_{}X}\sqrt{p(x)q(x)}是Bhattacharyya系数。 汉明距离两个等长字符串s1与s2之间的汉明距离定义为将其中一个变为另外一个所需要作的最小替换次数。例如字符串“1111”与“1001”之间的汉明距离为2。应用:信息编码(为了增强容错性,应使得编码间的最小汉明距离尽可能大)。 夹角余弦几何中夹角余弦可用来衡量两个向量方向的差异,机器学习中借用这一概念来衡量样本向量之间的差异。 在二维空间中向量$A(x1,y_1)$与向量$B(x_2,y_2)$的夹角余弦公式:cos\theta=\frac{x_1x_2+y_1y_2}{\sqrt{x_1^2+y_1^2}\sqrt{x_2^2+y_2^2}}两个n维样本点$a(x{11},x{12},\ldots,x{1n})$和$b(x{21},x{22},\ldots,x_{2n})$的夹角余弦:cos\theta=\frac{a*b}{|a||b|}夹角余弦取值范围为[-1,1]。夹角余弦越大表示两个向量的夹角越小,夹角余弦越小表示两向量的夹角越大。当两个向量的方向重合时夹角余弦取最大值1,当两个向量的方向完全相反夹角余弦取最小值-1。 杰卡德相似系数两个集合A和B的交集元素在A,B的并集中所占的比例,称为两个集合的杰卡德相似系数,用符号J(A,B)表示。杰卡德相似系数是衡量两个集合的相似度一种指标。J(A,B)=\frac{|A\cap{}B|}{|A\cup{}B|}与杰卡德相似系数相反的概念是杰卡德距离:J{\delta}(A,B)=1-J(A,B)=\frac{|A\cup{}B|-|A\cap{}B|}{|A\cup{}B|} 皮尔逊系数在统计学中,皮尔逊积矩相关系数用于度量两个变量X和Y之间的相关(线性相关),其值介于-1与1之间。通常情况下通过以下取值范围判断变量的相关强度: 0.8-1.0 极强相关0.6-0.8 强相关0.4-0.6 中等程度相关0.2-0.4 弱相关0.0-0.2 极弱相关或无相关 距离小结简单说来,各种“距离”的应用场景简单概括为, 空间:欧氏距离,路径:曼哈顿距离,国际象棋国王:切比雪夫距离,以上三种的统一形式:闵可夫斯基距离,加权:标准化欧氏距离,排除量纲和依存:马氏距离,向量差距:夹角余弦,编码差别:汉明距离,集合近似度:杰卡德类似系数与距离,相关:相关系数与相关距离。 常见问题余弦相似度 与 欧式距离的区别与联系 区别: 欧式距离和余弦相似度都能度量2个向量之间的相似度,但是欧式距离从2点之间的距离去考量,余弦相似从2个向量之间的夹角去考量。举例如下: 假设 2人对三部电影的评分分别是 A = [3, 3, 3] 和 B = [5, 5, 5] 那么2人的欧式距离是 根号12 = 3.46, A、B的余弦相似度是1(方向完全一致)。 从上例可以发出,2人对三部电影的评价趋势是一致的,但是欧式距离并不能反映出这一点,余弦相似则能够很好地反应。余弦相似可以很好地规避指标刻度的差异,最常见的应用是计算文本的相似度 。 联系: 归一化后计算的欧式距离是关于余弦相似的单调函数,可以认为归一化后,余弦相似与欧式距离效果是一致的(欧式距离越小等价于余弦相似度越大)。 因此可以将 求余弦相似转为求欧式距离 ,余弦相似的计算复杂度过高,转为求欧式距离后,可以借助KDTree(KNN算法用到)或者BallTree(对高维向量友好)来降低复杂度。 比如:$A(x_1,y_1)$,$B(x_2,y_2)$,余弦定义:$cos(\theta) = \frac{}{|A||B|}$。 归一化后 A\left(\frac{x{1}}{\sqrt{x{1}^{2}+y{1}^{2}}}, \frac{y{1}}{\sqrt{x{1}^{2}+y{1}^{2}}}\right), B\left(\frac{x{2}}{\sqrt{x{2}^{2}+y{2}^{2}}}, \frac{y{2}}{\sqrt{x{2}^{2}+y{2}^{2}}}\right) 余弦相似度: \cos =\frac{x{1}}{\sqrt{x{1}^{2}+y{1}^{2}}} \times \frac{x{2}}{\sqrt{x{2}^{2}+y{2}^{2}}}+\frac{y{1}}{\sqrt{x{1}^{2}+y{1}^{2}}} \times \frac{y{2}}{\sqrt{x{2}^{2}+y{2}^{2}}} 欧氏距离: \begin{aligned} e u c &=\sqrt{\left(\frac{x{1}}{\sqrt{x{1}^{2}+y{1}^{2}}}-\frac{x{2}}{\sqrt{x{2}^{2}+y{2}^{2}}}\right)^{2}+\left(\frac{y{1}}{\sqrt{x{1}^{2}+y{1}^{2}}}-\frac{y{2}}{\sqrt{x{2}^{2}+y{2}^{2}}}\right)^{2}} \ &=\sqrt{2-2 \times \cos } \end{aligned} 参考 [机器学习——几种距离度量方法比较](

0

深度学习

ㅤ1574493559·2021-07-19 09:51 0 阅读 668
文章图解KNN
开篇k-近邻算法是比较简单的一种机器学习算法,其核心思想可以用一句话来概括:近朱者赤,近墨者黑。 在具体介绍该算法之前,先通过一个栗子对该算法做一个感性上的认识。 Python爱好者 or C爱好者? 上图中,每一个形状(三角形,圆形)都代表了一个人。总共有两种形状,说明这些人总共可以分为两类:Python爱好者、C爱好者。 三角形一共有7个,代表喜欢写Python的总共有7人; 圆形一共有6个,代表喜欢写C语言的总共有6人。 现在,突然来了一个不知道是喜欢写Python还是C语言(并且只可能属于其中之一)的人——-五角星,要求你来判定这个人所属的类别。 emm… 你可能会说,那看看图上距离这个人(五角星)最近的几个人所属类别就可以了啊!比如就看距离这个人最近的3个人:其中有两个人喜欢写Python,而只有一个人喜欢写C语言(如下图所示) 按照少数服从多数的原则,将这个新来的人(五角星)归类到三角形(Python爱好者)类别就搞定啦! 最终分类结果 上面的栗子其实就使用了k-近邻算法的思想。现在,让我们对该算法做一个完整的定义。 什么是k-近邻算法 ?k-近邻算法是一种有监督的机器学习算法,对于有监督的机器学习算法来说,其训练数据集的一般格式如下(0:类别1;1:类别2): 当n取4,m取3时,就是一个含有4个样本,每个样本有3个特征的数据集了,如下图 k-近邻算法的核心思想为:对于一个给定的训练集,当新的样本到来时,找到训练集中与新样本距离最近的k个样本,然后查看这k个样本所属类别,并将新样本归类到这k个样本中大多数样本所属类别中。 没错,k-近邻算法的思想就是这么简洁。接下来,我们将用Python来实现该算法。 用Python实现k-近邻算法首先明确,k-近邻算法中的k是我们自己指定的(当然k的选取是有技巧的,这个稍后会说,这里我们仅仅关心如何用Python实现该算法),训练数据集也是自己的(废话,训练集肯定要自行准备的啊),所以有两个参数需要我们自行传入,其一为k,其二为训练集data。 在实现k-近邻算法之前,先自行定义训练集: class DataSet(): feature=np.array([[2.3,2.4], [0.1,0.2], [2.4,2.3], [0.2,0.1], [0.2,0.2], [2.4,2.4] ]) target=np.array([1,0,1,0,0,1]) 然后查看下训练数据集的分布(0和1代表样本所属的类别) data=DataSet() plt.scatter(data.feature[:,0],data.feature[:,1],c=data.target) for i in range(len(data.feature)): plt.text(data.feature[i,0], data.feature[i,1],data.target[i], fontsize=8, color = "b") plt.show() 现在开始实现k-近邻算法: class MyKNN(): def __init__(self,data,k): self.samples_feature=data.feature#样本特征 self.samples_target=data.target#样本所属类别 self.k=k 由于k-近邻算法涉及到了距离,因此需要写一个计算距离的方法。距离的度量方式有很多,这里我们采用大家最为熟悉的L2距离,也就是欧氏距离: ​ 欧式距离公式 def cal_dist(self,sample1,sample2): return np.sqrt(np.sum((sample-sample)**2)) 接下来是k-近邻算法的核心实现步骤: 1.分别计算新的样本与训练集中所有样本之间的距离;2.按照距离从小到大排序;3.选取距离新样本最近的k个距离及其对应样本所属类别;4.将新样本归类到与其距离最近的k个样本中大多数样本所属类别。 def run(self,new_sample_feature): #对于每一个样本的特征 all_dists=[] for i, sample_feature in enumerate(self.samples_feature): #1.分别计算新的样本与训练集中所有样本之间的距离 dist=self.cal_dist(sample_feature,new_sample_feature) all_dists.append((dist,self.samples_target[i])) #2.按照距离从小到大排序 sorted_dist_with_target=sorted(all_dists,key=lambda x:x[0]) #3.选取距离新样本最近的k个 top_k=sorted_dist_with_target[0:self.k] #4.统计这k个样本中大多数样本所属类别 dic={} for item in top_k: if item[1] in dic: dic[item[1]]+=1 else: dic[item[1]]=1 result=sorted(dic.items(),key=lambda x:x[1])[-1][0] return result 这样,我们就实现了k-近邻算法,完整代码如下: import numpy as np class MyKNN(): def __init__(self,data,k): self.samples_feature=data.feature#样本特征 self.samples_target=data.target#样本所属类别 self.k=k #计算两个样本之间的欧氏距离(L2距离) def cal_dist(self,sample1_feature,sample2_feature): return np.sqrt(np.sum((sample1_feature-sample2_feature)**2)) def run(self,new_sample_feature): #对于每一个样本的特征 all_dists=[] for i, sample_feature in enumerate(self.samples_feature): #分别计算新的样本与训练集中所有样本之间的距离 dist=self.cal_dist(sample_feature,new_sample_feature) all_dists.append((dist,self.samples_target[i])) #按照距离从小到大排序 sorted_dist_with_target=sorted(all_dists,key=lambda x:x[0]) #选取距离新样本最近的k个 top_k=sorted_dist_with_target[0:self.k] #统计这k个样本中大多数样本所属类别 dic={} for item in top_k: if item[1] in dic: dic[item[1]]+=1 else: dic[item[1]]=1 result=sorted(dic.items(),key=lambda x:x[1])[-1][0] return result 测试k-近邻算法我们前面已经实现了k-近邻算法,那么它的分类能力究竟如何呢? 现在,自定义一个测试集,用刚刚实现的k-近邻算法对测试集样本进行分类(这里的k不妨取3): test_samples_feature=np.array([[2.4,2.5], [0.3,0.1], [2.3,1.9], [0.1,0.3] ]) for i,item in enumerate(test_samples_feature): print('第{}个样本所属类别为{}'.format(i+1,knn.run(item))) 输出: 第1个样本所属类别为1 第2个样本所属类别为0 第3个样本所属类别为1 第4个样本所属类别为0 那这个结果可不可靠呢? 我们需要评估一下。千言万语不及一张图,所以这里我们还是用可视化的方式来演示。 在训练数据可视化结果图的基础上,将测试数据也绘制在这张图上,完整代码如下: data=DataSet() #训练数据 plt.scatter(data.feature[:,0],data.feature[:,1],c=data.target) for i in range(len(data.feature)): plt.text(data.feature[i,0], data.feature[i,1],data.target[i], fontsize=8, color = "b") #测试数据 plt.scatter(test_samples_feature[:,0],test_samples_feature[:,1]) for i in range(len(test_samples_feature)): plt.text(test_samples_feature[i,0], test_samples_feature[i,1], 'sample{}:\n'.format(i+1)+str((test_samples_feature[i,0],test_samples_feature[i,1])), fontsize=10, color = "r") plt.show() 观察上图,当k取3时: 与样本1距离最近的3个样本中,大多数样本都是属于类别1的(本例是特例,全部3个样本都属于类别1),因此样本1应该归类到类别1; 与样本2距离最近的3个样本中,大多数样本都是属于类别0的,因此样本2应该归类到类别0; 与样本3距离最近的3个样本中,大多数样本都是属于类别1的,因此样本3应该归类到类别1; 与样本4距离最近的3个样本中,大多数样本都是属于类别0的,因此样本4应该归类到类别0。 所以,最终的分类结果应该是:[1,0,1,0],这与程序跑出来的结果是对应的,从而我们实现的k-近邻算法是成功的。 更多关于k-近邻算法的使用技巧 k值通常取奇数,这是因为,当k取偶数,比如k=2时,有可能距离新样本最近的2个样本分别属于两个不同的类别,此时无法判定新样本所属类别;k不宜过大,也不宜过小,通常采用交叉验证的方式进行k值的选取;同大多数机器学习算法一样,在实际应用k-近邻算法时,为了减少特征的量纲不同而导致的各特征重要程度不一致现象,往往在将数据喂入机器学习算法之前,先对数据做归一化或者标准化等预处理工作。举个例子,对于具有3个特征的两个样本:[0.2,0.3,999], [0.3,0.2, 899],由于第三个特征数值相对于前两个特征较大,因此在计算这两个样本的距离时,第三个特征将主导最后的距离计算结果,而前两个特征的作用几乎被忽略不计了,这样的话,就相当于前两个特征传递的信息丢失了,这可不是我们所希望的!因此这一步很有必要。 参考: [1] [李航-统计学习方法][2] [Peter Harrington-机器学习实战]

0

机器学习

Python3

abccc·2021-07-14 17:08 0 阅读 1.6k
文章图解kd-tree
开篇在讲解k-近邻算法的时候,我们提供的思路是:对于新到来的样本,计算该样本与训练集中所有样本之间的距离,选取训练集中距离新样本最近的k个样本中大多数样本的类别作为新的样本的类别。 也就是说,每次都要计算新的样本与训练集中全部样本的距离。但是,在实际应用中,训练集的样本量和特征维度都是比较庞大的,这就导致该算法不得不在计算距离上花费大量的时间,那有没有什么方法可以在时间开销上对之前的k-近邻算法进行优化呢? 采用以空间来换时间的思想,就引出了今天的主角:kd树。 构造kd树kd树是一种二叉树,它可以将k维特征空间中的样本进行划分存储,以便实现快速搜索。 一头雾水?没关系,来看一个经典的构造kd树的例子。 现给定一个二维的训练集: T={(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)} 要求构造一个平衡kd树 第一步,选取第0个维度作为被划分的坐标轴,并按照第0个维度从小到大排列全部样本,得到:{(2,3),(4,7),(5,4),(7,2),(8,1),(9,6)} 第二步,找到第0个维度的中位数对应的样本。注意,这里的中位数与我们之前认知的中位数有些不同,具体表现在:对于本例,第0个维度排序后分别为:2,4,5,7,8,9。按道理中位数应该是(5+7)/2=6,但是,训练集的第0个维度中并没有6,所以,我们需要选取距离6最近的出现在训练集的第0个维度中的数字作为中位数,这里,5和7都是可以的。为了便于编程,我们就统一使用下标较大的位置的数字了: 6//2=3,所以最终选择下标为3位置的数字,即数字7作为第0个维度的中位数。 第三步,以第二步中选取的中位数为基准,并作为当前划分的父节点。将从小到大排序好的样本序列进行划分:第0个维度小于基准的样本被划分到当前划分的父节点的左子树,第0个维度大于基准的样本被划分当前划分的父节点的右子树。此时得到如下的树: 第四步,选取新的维度,按照公式 “新的划分维度=(上一次使用的维度+1)mod 特征总维数” ,得到新的维度为:(0+1) mod 2 = 1 。 于是以维度1替换维度0,重复第一步到第三步: 对左子树{(5,4),(2,3),(4,7)}按照特征的第一个维度从小到大排序:{(2,3),(5,4),(4,7)},确定中位数下标为3//2=1,所以数字4为中位数;将(5,4)作为当前划分的父节点,第一维度大于4的作为其左子树,第一维度小于4的作为其右子树; 对右子树{(9,6),(8,1)}按照特征的第一个维度从小到大排序:{(8,1),(9,6)},确定中位数下标为2//2=1,所以数字6为中位数;将(9,6)作为当前划分的父节点,第一维度大于6的作为其左子树,第一维度小于6的作为其右子树; 此时得到的树如下: 由于此时训练集中所有子区域都已划分完毕(任一子区域中不含样本点),因此kd树就构造完成了。在上面的过程中,每分一次岔,就对应特征空间的一次划分(叶子节点的左右孩子都为空,但这里仍可以看成是一种特殊的分叉【左右分支都为空】)最终整个特征空间被划分如下: 现在来用Python实现上述过程。首先定义每个节点的数据结构: class Node(): def __init__(self,lchild,rchild,value): self.lchild=lchild#节点的左子树 self.rchild=rchild#节点的右子树 self.value=value#节点的数值 然后初始化一个KD树的类: class KDTree(): def __init__(self,data): self.dims=len(data[0])#训练集总特征数 接下来到了构建kd树的核心步骤,从之前的例子中,可以总结出我们的思路: 创建kd树的过程是递归的,所以我们可以递归地构造之:(1) 递归地构造左子树;(2) 递归地构造右子树;(3) 构造父节点,将其lchild与构造好的左子树连接,将其rchild与构造好的右子树连接。除此之外,还有一些辅助的方法,比如求指定维度的中位数,计算下一个划分维度,将会写成单独的方法以使得创建树的代码更加具有可读性。最后,不要忘了递归出口:被划分的子区域没有样本存在时,就退出. def create_kdtree(self,current_data,split_dim): #设置递归出口:当全部样本划分完毕时就退出 if len(current_data)==0: return None mid=self.cal_current_medium(current_data)#计算中位数所在下标 data_sorted=sorted(current_data,key=lambda x:x[split_dim])#按照切分维度从小到大排序 #下面三句代码本质上就是二叉树的后序遍历 lchild=self.create_kdtree(data_sorted[0:mid],self.cal_split_dim(split_dim))#递归地构造左子树 rchild=self.create_kdtree(data_sorted[mid+1:],self.cal_split_dim(split_dim))#递归地构造右子树 return Node(lchild,rchild,data_sorted[mid])#连接从根节点出发的左右子树,并返回 #计算下一个划分维度 def cal_split_dim(self,split_dim): return (split_dim+1) % self.dims #计算当前维度中位数所在下标 def cal_current_medium(self,current_data): return len(current_data)//2 完整的kd树构造代码如下: class KDTree(): def __init__(self,data): self.dims=len(data[0])#训练集总特征数 def create_kdtree(self,current_data,split_dim): #设置递归出口:当全部样本划分完毕时就退出 if len(current_data)==0: return None mid=self.cal_current_medium(current_data)#计算中位数所在下标 data_sorted=sorted(current_data,key=lambda x:x[split_dim])#按照切分维度从小到大排序 #下面三句代码本质上就是二叉树的后序遍历 lchild=self.create_kdtree(data_sorted[0:mid],self.cal_split_dim(split_dim))#递归地构造左子树 rchild=self.create_kdtree(data_sorted[mid+1:],self.cal_split_dim(split_dim))#递归地构造右子树 return Node(lchild,rchild,data_sorted[mid])#连接从根节点出发的左右子树,并返回 #计算下一个划分维度 def cal_split_dim(self,split_dim): return (split_dim+1) % self.dims #计算当前维度中位数所在下标 def cal_current_medium(self,current_data): return len(current_data)//2 运行下面的代码,就构造好了一棵kd树: dataset = np.array([[2,3],[4,7],[5,4],[7,2],[8,1],[9,6]])#构建训练数据集 kdtree = KDTree(dataset).create_kdtree(dataset,0)#创建KD树,以特征的第0个维度开始做划分 搜索kd树这里仅实现最近邻搜索。所谓最近邻,就是k-近邻中k取1时的特殊情况。我们还是以具体的例子进行说明。基于上面构造好的kd树,现在来搜索样本点(2, 4.5)的最近邻点。先把之前的图搬过来,对照该图阅读以下步骤会更容易理解: 从根节点开始: 首先来到第一层:在构造kd树时,由于(7,2)是根据维度0进行划分的,因此需要比较(2,4.5)与(7,2)的第0个维度的大小。由于2<7,因此接下来将搜索(7,2)的左子树(也就是(5,4)节点),反映到划分图上,就是去”过点(7,2)的垂直于横轴的划分线”的左侧进行接下来的搜索;然后来到第二层:在构造kd树时,由于(5,4)是根据维度1进行划分的,因此需要比较(2,4.5)与(5,4)的第1个维度的大小。由于4.5>4,因此接下来将搜索(5,4)的右子树(也就是(4,7)节点),反映到划分图上,就是去”过点(5,4)的垂直于纵轴的划分线”的上侧进行接下来的搜索;接着来到第三层:由于(4,7)已经是叶子节点,无左右孩子,所以从根节点(7,2)到叶子节点的搜索就完成了,当前的最近邻节点就是最后到达的叶子节点,也就是(4,7)。现在,开始从叶子节点(4,7)向上往根节点进行搜索(这也称之为回溯):(1)以(2,4.5)为中心,以(2,4.5)到当前最近邻点(4,7)的距离为半径,画一个圆(这里特征是二维的,所以是圆。一般的,对于高维特征的情况,画出来的是一个超球面),真正的最近邻点一定包含在这个圆的内部。于是当前最近邻点是(4,7),最近距离为半径长度=3.2015; (2) 从叶子节点(4,7)返回其父节点(5,4),计算(5,4)与(2,4.5)的距离为3.0413,而3.0413<3.2015,因此当前最近邻点被更新为(5,4),最近距离被更新为3.0413; (3) 返回计算父节点(5,4)的另一子节点(这里也就是(2,3)),计算其与目标点(2,4.5)的距离为1.5,而1.5<3.0413,因此当前最近邻点被更新为(2,3),最近距离被更新为1.5; (4) 此时父节点(5,4)的另一子节点已经搜索完毕,继续向上回溯搜索那些没有被回溯过的节点,于是来到根节点(7,2),计算(7,2)与(2,4.5)的距离为5.5901,而5.5901>1.5,因此当前最近邻点不变,最近距离也不变。由于已经回溯到了根节点,整个搜索就完毕了,当前最近邻点就是我们最终要找的最近邻点,即(2,3)。 现在,让我们用程序来实现以上的搜索过程。基于构造kd树的代码,需要增加搜索的方法以及一些小的变动,具体如下: 由于在前向搜索的过程中,需要知道每个节点是根据哪个维度进行划分的,因此给每个节点增加一个维度属性:split_dimclass Node(): def __init__(self,lchild,rchild,value,split_dim): self.lchild=lchild#节点的左子树 self.rchild=rchild#节点的右子树 self.value=value#节点的数值 self.split_dim=split_dim#用来做划分的维度 为了便于返回最近邻点和最近距离,将这两个属性添加到kd树的属性中:class KDTree(): def __init__(self,data): self.dims=len(data[0])#总特征数 self.nearest_point=None self.nearest_distance=np.inf#初始化为无穷大 由于涉及到了距离的比较,因此增加计算两点之间距离的方法:#计算两点之间的欧氏距离 def cal_dist(sample1,sample2): return np.sqrt(np.sum((sample1-sample2)**2)) 算法将从根节点开始搜索,由于是递归的,所以这里可以先写一个辅助的递归入口函数,真正实现递归的算法写在另一个方法中:#element:目标节点;root:kd树的根节点 def get_nearest(self,root,element): search(root,element)#递归地搜索 return self.nearest_point,self.nearest.dist 现在来实现递归搜索的过程:def search(self,node,element): if node is None: return #计算当前划分维度上目标节点与当前节点的单一维度上的距离 dist = node.value[node.split_dim] - element[node.split_dim] #前向搜索 if dist>0:#当前节点在目标节点的上侧或左侧(在二维空间中) self.search(node.lchild,element)#递归地搜索左子树 else:#否则,当前节点在目标节点的下侧或右侧(在二维空间中) self.search(node.rchild,element)#递归地搜索右子树 #计算目标节点与当前节点的欧氏距离 curr_dist = self.cal_dist(node.value,element) #更新最近邻节点 if curr_dist < self.nearest_dist: self.nearest_dist = curr_dist self.nearest_point = node #print(self.nearest_point.value) #回溯 #比较“最近距离”是否超过“目标节点与当前节点在当前划分维度上的距离”,超过了就说明可能在当前节点的另一侧子树中存在更近的点,所以需要到当前节点的另一侧子树中去搜索 if self.nearest_dist > abs(dist): #由于是去当前节点的另一侧子树中进行搜索,因此正好与之前的前向搜索相反 if dist>0: self.search(node.rchild,element) else: self.search(node.lchild,element) 完整代码如下:```pythonimport numpy as npclass Node():def init(self,lchild,rchild,value,split_dim):self.lchild=lchild#节点的左子树 self.rchild=rchild#节点的右子树 self.value=value#节点的数值 self.split_dim=split_dim#用来做划分的维度 class KDTree(): def init(self,data): self.dims=len(data[0])#总特征数 self.nearest_point=None self.nearest_dist=np.inf#初始化为无穷大 def create_kdtree(self,current_data,split_dim): #设置递归出口:当全部样本划分完毕时就退出 if len(current_data)==0: return None mid=self.cal_current_medium(current_data)#计算中位数所在下标 data_sorted=sorted(current_data,key=lambda x:x[split_dim])#按照切分维度从小到大排序 #下面三句代码本质上就是二叉树的后序遍历 lchild=self.create_kdtree(data_sorted[0:mid],self.cal_split_dim(split_dim))#递归地构造左子树 rchild=self.create_kdtree(data_sorted[mid+1:],self.cal_split_dim(split_dim))#递归地构造右子树 return Node(lchild,rchild,data_sorted[mid],split_dim)#连接从根节点出发的左右子树,并返回 #计算下一个划分维度 def cal_split_dim(self,split_dim): return (split_dim+1) % self.dims #计算当前维度中位数所在下标 def cal_current_medium(self,current_data): return len(current_data)//2 #计算两点之间的欧氏距离 def cal_dist(self,sample1,sample2): return np.sqrt(np.sum((sample1-sample2)**2)) #传入kd树的根节点root和待搜索的点element,搜索element的最近邻点 def search(self,node,element): if node is None: return #计算当前划分维度上目标节点与当前节点的单一维度上的距离 dist = node.value[node.split_dim] - element[node.split_dim] #前向搜索 if dist>0:#当前节点在目标节点的上侧或左侧(在二维空间中) self.search(node.lchild,element)#递归地搜索左子树 else:#否则,当前节点在目标节点的下侧或右侧(在二维空间中) self.search(node.rchild,element)#递归地搜索右子树 #计算目标节点与当前节点的欧氏距离 curr_dist = self.cal_dist(node.value,element) #更新最近邻节点 if curr_dist < self.nearest_dist: self.nearest_dist = curr_dist self.nearest_point = node #print(self.nearest_point.value) #回溯 #比较“最近距离”是否超过“目标节点与当前节点在当前划分维度上的距离”,超过了就说明可能在当前节点的另一侧子树中存在更近的点,所以需要到当前节点的另一侧子树中去搜索 if self.nearest_dist > abs(dist): #由于是去当前节点的另一侧子树中进行搜索,因此正好与之前的前向搜索相反 if dist>0: self.search(node.rchild,element) else: self.search(node.lchild,element) def get_nearest(self,root,element): self.search(root,element) return self.nearest_point.value,self.nearest_dist 现在来测试一下: ```python dataset = np.array([[2,3],[4,7],[5,4],[7,2],[8,1],[9,6]])#构建训练数据集 kdtree = KDTree(dataset)#实例化一个kd树对象 root=kdtree.create_kdtree(dataset,0)#创建KD树,且以特征的第0个维度开始做划分,最终返回的是根节点 nearest_point,nearest_dist=kdtree.get_nearest(root,[2,4.5])#搜索[2,4.5]的最近邻点 print('最近邻点:{}\n最近距离:{}'.format(nearest_point,nearest_dist)) 运行结果: 最近邻点:[2 3] 最近距离:1.5 这和之前我们推导的结果是一致的。 最后,感谢互联网上的优秀资源,给本文提供了大量的参考。 参考资料: [1] [李航-统计学习方法][2] [https://www.cnblogs.com/ssyfj/p/13053055.html#https://blog.csdn.net/app_12062011/article/details/51986805] 代码参考自此,感谢原作者[3] [https://www.bilibili.com/video/BV1gy4y1z7Pw?from=search&seid=13871841353290001041][4] [https://www.bilibili.com/video/BV1o541137N5?from=search&seid=13871841353290001041]

0

机器学习

Python3

abccc·2021-07-14 17:07 0 阅读 993
文章华为算法岗武功秘籍(上)
华为面经目录 1 华为面经汇总资料2 华为面经涉及基础知识点3 华为面经涉及项目知识点4 数据结构与算法分析相关知识点5 编程高频问题:Python&C/C++方面6 操作系统高频问题:数据库&线程等7 技术&产品&开放性问题 1 华为面经汇总资料 1.1 面经汇总参考资料 ① 参考资料: (1)牛客网:华为面经-172篇,网页链接 (2)知乎面经:点击进入查看 (3)面试圈:点击进入查看 ② 面经框架&答案&目录&心得: (1)面经框架及参考答案:点击进入查看 (2)大厂目录及整理心得:点击进入查看 1.2 面经涉及招聘岗位 (1)实习岗位类【华为云EI实习岗】、【计算机视觉实习生】、【华为杭研院Cloud&AI昇腾计算产品部算法实习】 (2)全职岗位类【机器学习算法工程师】、【终端部门算法工程师】、【开发硬件算法工程师】、【华为上研算法工程师】、【AI应用研究中心工程师】、【华为云视频内容分析】、【华为消费者bg算法工程师】、【Cloud Bu人工智能工程师】、【华为南京研究院算法工程师】、【华为成都研究院算法工程师】、【华为AI算法工程师】、【华为西安研究院算法工程师】、【华为南京NLP算法工程师】、【华为自动驾驶算法工程师】、【华为射频算法工程师】、【华为消费云服务部AI工程师】、【华为数据存储与机器视觉产品线智能协作产品部AI工程师】、【华为智能车BU AI算法工程师】、【图像算法工程师】、【音频算法工程师】、【搜索推荐算法工程师】、【圣无线的通用软件开发工程师】、【成都传送/无线部门通信算法工程师】、【华为数据存储AI工程师】、【昇腾计算产品部AI工程师】(机器学习方向)】 1.3 面试流程时间安排 PS:以上流程为大白总结归纳所得,以供参考。 其他注意点: ● 有些人在第一面之前,还会有机试和性格测试● 有些区域是技术面+机试性格测试+三面BOSS面● 有些人是先综合面,再HR面 1.4 华为面经面试心得汇总 ★ 华为特别重视底层原理,和其他互联网公司不一样。★ 华为的面试看面试官吧!有的人会被很多技术的,有的只是聊聊人生和项目。★ 总结一下三场面试,需要准备好编程相关的问题,机器学习相关的问题,自己方向最新的技术。另外,三场面试都着重问了项目,可能我比较菜,没有发过论文。自己对项目的细节一定要十分了解,这样就不用慌了,随便问都能答上来。★ 每个区域的招聘流程稍微有点差异,不过一般分为基础面试、综合面试:基础面试基本就是聊项目经历或者实习经历,另外有些会从产品的角度出发,出一些发散性思维的题目,不怎么为难你 ,主要问项目经历综合面试主要谈性格、对华为的认识、为什么想加入华为;主要看重承担压力的能力,表现的性格开朗就Okay了。★ 有的时候,面试很难,有的时候很简单,所以还是看人,但是最好认真准备,以不变应万变。★ 聊简历上的项目,每次说到某个点会继续深入问一下,但挖的不深。我面的那个面试官是做人脸识别和指纹识别的,最后问我怎么识别是照片还是真人,我以为都只能拍一张照片,就说了一些用深度,或者阴影和光照等解决之类的,但其实是可以拍很多张的,可以根据运动判断,所以跟面试官好好沟通真的很重要! 2 华为面经涉及基础知识点 2.1 图像处理基础 2.1.1 讲解相关原理 ● 传统图像处理的canny算子 2.2 深度学习:CNN卷积神经网络方面 2.2.1 讲解相关原理 2.2.1.1 卷积方面 ● CNN中1*1卷积核的作用?● 介绍一个熟悉的CNN模型,卷积怎么反向传播?● CNN基本组成,什么是感受野, 反向传播原理?● 膨胀卷积原理●空洞卷积相比普通卷积的不同之处,如果特征图很小,这时要用空洞卷积就会加很多padding,增加很多无用信息,怎么处理这种情况? 2.2.1.2 池化方面 ● 池化层的作用?(拓展讲了种类、反向传播,以及 pytorch 特有的自适应池化) 2.2.1.3 网络结构方面 ● 简述MobileNet的V1,V2,V3的区别?● vgg、resnet、densenet之间的比较?● 画一下MobileNet网络结构● resnet和denseNet的网络结构,以及为什么这样设计?● ResNet的作用?● 认识哪些常用网络,是为了解决什么问题所提出的?● 为什么要用轻量级的网络?shufflenetv2相比v1有什么改进? 2.2.1.4 其他方面 ● 简单的介绍一下CNN,及它的发展和应用?● 自己写网络模型时,是手动搭,还是复现或调库?自己有没有优化或者自己搭建新模型,描述一下?● 梯度消失/爆炸产生原因,及解决方法? 2.2.2 公式推导 ● 写一下了batch norm的公式?● Softmax等层的原理(公式)写一下?● 推导神经网络链式法则 2.2.3 手写算法代码 ● 手推卷积过程 2.3 深度学习:RNN递归神经网络方面 2.3.1 讲解相关原理 ● 简单的介绍一下RNN,及它的发展和应用?● RNN,LSTM,GRU的异同?● 介绍LSTM及其变种?● 解释LSTM原理,LSTM的结构描述一下,超参数说一下?● LSTM为了解决长依赖问题,引入了三个门,分别啥意思?● 能否详细的介绍LSTM模型的结构和内部的运行过程?● 双向LSTM比LSTM到底好在哪?● LSTM为什么可以避免过拟合?● LSTM哪个门用到了上一状态? 2.3.2 手绘网络原理 ● 画出LSTM的结构图,写公式 2.4 深度学习CNN&RNN通用的问题 2.4.1 基础知识点 ● 不平衡样本怎么处理?● Transformer相比于RNN你认为有哪些改进?● 怎么做的数据增广?● attention怎么做? 2.5 传统机器学习方面 2.5.1 讲解相关原理 2.5.1.1 数据准备 无 2.5.1.2 特征工程 ① 特征降维 ● SVD与PCA的关系? ② 特征选择 ● 特征选择的方法?(这里建议分 filter, wrapper, embedded 来讲,我只是说了PCA,LDA,L1) 2.5.1.3 有监督学习-分类和回归方面 ① 分类回归树(集成学习) ● 集成学习了解吗?XGBOOST/GBDT简单介绍,区别?● bagging和boosting,stacking区别,分别的原理?A.基于bagging:随机森林● 为什么随机森林能降低方差?B.基于boosting:Adaboost、GDBT、XGBoost● 树模型和熵介绍,为什么xgboost效果好?● xgb和gbdt的区别?● GBDT、RF 有什么异同?各适用于什么样的情况?● 介绍xgb,lgb? ② 逻辑回归LR ● 线性回归解析解的推导(三种方法) ③ SVM(支持向量机) ● 介绍一下SVM,介绍了核函数的种类、支持向量、超平面、软间隔、Hinge Loss?● svm 优缺点 ④ 朴素贝叶斯(Naive Bayes) ● 贝叶斯模型?(这里我顺着讲了朴素贝叶斯、逻辑回归最大似然推损失函数的过程)● 解释极大似然估计,最大后验概率估计,解释核函数及其应用? ⑤ 决策树(DT) ● 决策树划分选择、树的复杂度、剪枝?● 决策树,随机森林原理? 2.5.1.4 无监督学习-聚类方面 ● knn与k-means的区别?● k-means和DBSCAN 的对比,k 的选取,提速,聚类方法的评估?● 聚类算法如何提升性能?● K-mean算法的优缺点(简历中有提到K-means++)● DBSCAN的原理? 2.5.2 手推算法及代码 ● 手推SVM● 写一下贝叶斯公式● 写一下KL散度公式 2.6 深度学习&机器学习面经通用知识点 2.6.1 损失函数方面 ● 深度学习有哪些激活函数?为啥会有激活函数?● 为什么Relu的结构小于0的输出为0?这样有什么优点(防止梯度消失,稀疏性以及加快计算,当时没想到)什么缺点,如何改进,改进版relu的名字是什么(忘记了叫LRelu)?● MSE和交叉熵的区别 ,写交叉熵?● 手推交叉熵的求导 2.6.2 激活函数方面 ● Sigmoid与Softmax的区别与联系?● 说一下激活函数,relu和sigmoid区别? 2.6.3 网络优化梯度下降方面 ● sgd和adam的优缺点?● 什么是ADMM,为什么用ADMM,子问题为什么不用梯度下降求解?● 梯度下降为什么可以成功?(我回答的是损失函数是凸函数) 2.6.4 正则化方面 ● L1,L2符合哪种分布? 2.6.5 压缩&剪枝&量化&加速 ● 模型压缩的几种方法?(量化、剪枝、低秩分解等 )实际用过吗?● 量化的理解,有什么好处 ?● 加速优化的方法有哪些,剪枝如何操作,最近看过的论文,跟进的方法? 2.6.6 过拟合&欠拟合方面 ● 怎么判断过拟合与欠拟合?● 解决过拟合和欠拟合的办法?● 机器学习当中可能会有欠拟合过拟合的问题,怎么解决过拟合问题?● 对于传统的机器学习(rf,lr,svm)来说,一般靠引入正则化项来避免正则化问题,那么应用到深度学习里面,过拟合的解决方式主要有dropout、early-stopping、数据增强等 2.6.7 其他方面 ● 深度学习与传统方法的区别,深度学习为什么效果这么好?● 根据项目经历解释偏差-方差的权衡?● 数据不平衡怎么解决?● 说一下训练模型过程中可能遇到的问题以及解决方法?(这里我详细讲了梯度消失、爆炸,训练曲线不下降,过拟合,欠拟合的产生情况和解决方案) 来源:知乎 作者:江大白

0

Python3

AI小助手·2021-07-14 17:07 0 阅读 680
文章工业场景这些数据集,你值得拥有!
平台的数据集版块,共有165个不同类别,不同应用的数据集。 本周在此基础上,又上新10种工业场景相关的数据集,目前总共有172种。 第一部分:3D检测 (1)MVTec ITODD数据集 数据集图片: 数据集内容: MVTec ITODD是用于3D对象检测和姿态估计的数据集,重点关注工业设置和应用。 对于每个对象,只有一个实例的场景和有多个实例的场景(例如,模拟箱拣选)可用。每个场景使用每个3D传感器采集一次,每个灰度相机采集两次:一次带有随机投影图案,一次没有随机投影图案。 该数据集为工业场景中3D对象的检测和姿态估计提供了很好的基准。 数据集数量:MVTec ITODD数据集包含布置在800多个场景中的28个对象,并以其严格的3D变换标记为地面实况。场景由两个工业3D传感器和三个灰度相机进行观察,从而可以评估适用于3D,图像或组合形式的方法。 数据集功能:3D检测 下载链接:点击查看 (2)T-LESS数据集 数据集图片: 数据集内容:T-LESS数据集用于估计无纹理的刚性物体的6D姿势,即平移和旋转。该数据集包含30个与行业相关的对象,这些对象没有明显的纹理并且没有可辨别的颜色或反射率属性。该数据集的另一个独特属性是某些对象是其他对象的一部分。 T-LESS背后的研究人员已经选择了不同的方法来训练图像和测试图像。因此,该数据集中的训练图像在黑色背景下描绘了单个对象,而测试图像则来自二十个场景,且复杂程度不同。 本数据集的优势在于: (1)大量跟工业相关的目标; (2)训练集都是在可控的环境下抓取的; (3)测试集有大量变换的视角; (4)图片是由同步和校准的sensor抓取的; (5)准确的6D pose标签; (6)每个目标有两种3D模型; 数据集数量:数据集由三个同步的传感器获得,一个结构光传感器,一个RGBD sensor,一个高分辨率RGBsensor。 从每个传感器分别获得了3.9w训练集和1w测试集,此外为每个目标创建了2个3D model,一个是CAD手工制作的,另一个是半自动重建的。 训练集图片的背景大多是黑色的,而测试集的图片背景很多变,会包含不同光照、遮挡等等变换。 数据集功能:6D对象姿态估计、2D目标检测、图像分割、3D对象重建 下载链接:点击查看 第二部分:缺陷检测 (1)DeepPCB数据集 数据集图片: 数据集内容:DeepPCB数据集是一个共有PCB缺陷数据集,共包含1500组图像,每组图像,均包含无缺陷的模板图像和对齐的测试图像,并带有注释。 其中包括6种最常见的PCB缺陷类型的位置:开口,短路,咬伤,杂散,针孔和伪铜。 所有图像均来自线性扫描CCD,分辨率约为1毫米=48像素。 模板和测试图像的大小,约为16k x 16k像素,然后将它们裁剪为很多大小为640 x 640的子图像,并通过模板匹配技术进行对齐。 数据集数量:DeepPCB数据集包含1500个图像组 数据集功能:缺陷检测 下载链接:点击查看 (2)Kolektor表面缺陷数据集 数据集图片: 数据集内容:Kolektor表面缺陷数据集,是由Kolektor Group doo提供并对缺陷标注的图像数据集。在真实情况下,图像是在受控的工业环境中捕获的。 数据集数量:Kolektor数据集包括399张图片,其中52张有明显缺陷,347张图像没有任何缺陷。 原始图片的宽度为500像素,高度从1240至1270像素不等。 数据集功能:缺陷检测 下载链接:点击查看 (3)AITEX FABRIC数据集 数据集图片: 数据集内容:AITEX FABRIC数据集是一个纺织物缺陷图像数据集,包含7种不同纺织物的245张图像。其中无缺陷的图像140张,每种类型的织物20张。不同类型的缺陷图像,有105张图像。 图像尺寸为4096×256像素,缺陷图像的名称如下: nnnn_ddd_ff.png,其中nnnn是图像编号,ddd是缺陷代码,ff是结构代码。 比如缺陷图像,命名为: nnnn_ddd_ff_mask.png,其中白色像素代表缺陷图像的缺陷区域。 无缺陷图像的名称如下: nnnn_000_ff.png,其中缺陷代码替换为0000。 数据集数量:AITEX FABRIC数据集包含245张图像,其中140张无缺陷、105张有缺陷图像。 数据集功能:缺陷检测 下载链接:点击查看 (4)钢材表面NEU-CLS数据集 数据集图片: 数据集内容:钢材表面NEU-CLS数据集是一个由东北大学(NEU)发布的表面缺陷数据集,收集了热轧钢带的六种典型表面缺陷,即轧制氧化皮(RS),斑块(Pa),开裂(Cr),点蚀表面( PS),内含物(In)和划痕(Sc)。 数据集数量:数据集包含1800个灰度图像,六种不同类型的典型表面缺陷,每一类缺陷包含300个样本。 对于缺陷检测任务,数据集提供了注释,指示每个图像中缺陷的类别和位置。 数据集功能:缺陷检测 下载链接:点击查看 (5)天池铝型材表面瑕疵数据集 数据集图片: 数据集内容:在铝型材的实际生产过程中,由于各方面因素的影响,铝型材表面会产生裂纹、起皮、划伤等瑕疵,这些瑕疵会严重影响铝型材的质量。 传统人工肉眼检查十分费力,不能及时准确的判断出表面瑕疵,质检的效率难以把控。近年来,深度学习在图像识别等领域取得了突飞猛进的成果。 铝型材制造商迫切希望采用最新的AI技术来革新现有质检流程,自动完成质检任务,减少漏检发生率,提高产品的质量。 数据集数量:数据集里有1万份来自实际生产中有瑕疵的铝型材监测影像数据,每个影像包含一个或多种瑕疵。 数据集功能:瑕疵检测 下载链接:点击查看 来源:知乎 作者:江大白

0

Python3

AI小助手·2021-07-13 10:30 0 阅读 414
文章PSGAN:一秒仿妆~
BeautyGAN:提出了makeup loss等,后续PSGAN也基于这些loss。训练数据需要成对,数据集难收集。 Paired Cycle GAN:额外添加了一个判别器 Beauty Glow LADN… 以上方法不适用于in the wild 图像,且不能只调节局部,比如只给眼睛上妆。 PSGAN(CVPR2020)解决了这些痛点,并且PSGAN++做了进一步改进,对比如下: 由于PSGAN++还没开源代码,所以选择了有代码的PSGAN,同时作者也提供了预训练模型。 我用默认配置参数跑了下,发现并没有得到论文中的效果,调了些参数,比如生成器和判别器的学习率,效果也不是特别好。 使用预训练模型,就能够得到论文中的效果。可能自己还需要调更多参数吧~ 下面是关于PSGAN的论文要点整理。 网络结构PSGAN的生成模块包括3部分: Makeup distill network(MDNet)Attentive makeup morphing module(AMM module)Makeup Apply Network(MANet) reference image:上妆容图,假设在Y域source image:未上妆图,假设在X域 MDNetMDNet负责从reference image 中提取makeup style,并将其用两个makeup矩阵$\beta$和$\gamma$表示。 MDNet的网络结构和Stargan(CVPR2017)中的Encoder部分相同,它的输入是reference的特征图$V_y$(MDNet的输出),经过1x1卷积得到makeup矩阵$\beta$和$\gamma$。 MDNet可以将人脸不同部位,比如嘴巴和脸颊的特征分离开来。(怎么分开?) 图中右上角展示了这一过程。 AMM moduleAMM module通过考虑source和reference逐像素之间的相似性,解决了两者之间可能存在的pose,expression不一致的问题,因此经过MDNet从reference得到的$\beta$和$\gamma$不能直接拿来用,需要做进一步处理。 AMM module计算了一个attentive矩阵,记作$A$,它可以明确source和reference逐像素的映射关系,即source中的每一个像素是如何从reference对应像素通过morph得到的。 $A$是$HW*HW$的,$A_{ij}$表示source中第$i$个像素与reference中第$j$个像素之间的attentive value。 为什么是$A$是$HW*HW$的? 因为source和reference都是$HW$的,所以分别总共有$HW$个像素,又$A_{ij}$表示source中第$i$个像素与reference中第$j$个像素之间的attentive value,因此$A$是$H*W$的。 接下来介绍怎么计算$A$。 $A$的计算需要考虑两部分: similar relative positionsvisual similarities 作者指出,makeup transfer应该在referen和source中的similar relative positions处进行操作,比如嘴巴,脸颊,眼睛等,且在这些位置的像素之间的attentive value应该大一些。 作者使用facial landmarks作为ancho points,来找出这些similar relative positions。方法如下: 对于一张人脸,获取了68个关键点$l1,l_2,…,l{68}$,用$f(.)$和$g(.)$分别表示x轴和y轴坐标,则对于每一个像素$x_i$,有: 由于在不同图片中,脸部所占图片尺寸可能不一致,因此在计算attentive value时需要对$p_i$做归一化处理,具体地,论文中使用了2-范数:$\frac{p}{||p||}$. 但有时候,即使经过上述操作找对了similar relative positions,但是它们可能有不同的语义(个人理解:比如reference的额头被头发遮盖)。 因此,作者指出,在计算$A$(或者说在计算attentive value)时,除了考虑source和reference之间逐像素的similar relative positions外,还要考虑两者之间逐像素的visual similarities. 当然,similar relative positions是最主要的,visual similarities是辅助的,这一点可以通过设置不同权重来保证。 如图(B)所示,灰色和橘色的$CHW$块代表visual similarities,绿色块代表similar relative positions。 将similar relative positions和visual similarities在通道维度上做concat操作,得到两个块,让这两个块做矩阵乘法,再对乘法结果做softmax,就得到了$A$,公式如下: 从上述操作也可以看出,$A$的计算同时考虑了similar relative positions和visual similarities。 之前的$MDNet$已经从reference中提取出来makeup style,并用$\beta$和$\gamma$表示,但由于pose和expression不同等原因,导致不能直接用。因此需要用AMM module对其做一些处理。 现在已经有了attention matrix $A$,将其与$\beta$和$\gamma$结合,就能得到能够应用在source上的makeup style,即$\beta’$和$\gamma’$: $A_{ij}$是1*H*W的,$\gamma$是1*H*W的,因此上述公式相当于向量内积,得到的$\gamma_i’$是一个数;同理,$\beta_j’$也是一个数。 总共有$HW$个像素,因此$\beta’$和$\gamma’$的长度为HW,可reshape到$1H*W$。 然后,$\beta’$和$\gamma’$的shape被广播为$CHW$(复制C次,再concat起来),就得到了$B’$和$ Γ’$,它们将被输入到MANet。 MANetMANet使用了类似Stargan的Encoder-Decoder架构(之前的MDNet只使用了Stargan的Encoder,但MDNet和MANet的Encoder只是结构一样,并不共享参数)。 在MANet的Ecoder中,使用Instance Normalization将特征图映射到正态分布。 $B’$和$ Γ’$从Bottleneck处输入MANet,通过一个线性变换给source加上reference的makeup style,得到$V_x’$: V_x’= Γ’V_x+B’ 最后,将$V_x’$输入到MANet后续的Decoder部分,产生最终的makeup transfer结果。 损失函数loss沿用了BeautyGAN中的loss。 1. Adversarial loss 2. Cycle consistency loss 3. Perceptual loss 4. Makeup loss. 总的loss如下:

0

深度学习

机器学习

abccc·2021-07-12 10:43 0 阅读 447
没有更多了