-
Notifications
You must be signed in to change notification settings - Fork 0
/
content.json
1 lines (1 loc) · 437 KB
/
content.json
1
{"meta":{"title":"贾维斯的小屋-人工智能学习个人网站","subtitle":null,"description":"机器学习个人网站","author":"Xudong Li","url":"http://yoursite.com","root":"/"},"pages":[{"title":"NAS专栏","date":"2019-11-22T14:06:58.000Z","updated":"2020-08-03T10:17:58.000Z","comments":true,"path":"NAS专栏/index.html","permalink":"http://yoursite.com/NAS专栏/index.html","excerpt":"","text":"自动化机器学习(AutoML)最近变得越来越火,是机器学习下个发展方向之一。其中的神经网络结构搜索(NAS)是其中重要的技术之一。人工设计网络需要丰富的经验和专业知识,神经网络有众多的超参数,导致其搜索空间巨大。NAS即是在此巨大的搜索空间里自动地找到最优的网络结构,实现深度学习的自动化。自2017年谷歌与MIT各自在ICLR上各自发表基于强化学习的NAS以来,已产出200多篇论文,仅2019年上半年就有100多篇论文。此专栏以求尽可能多地汇集NAS相关的资料,帮助大家学习NAS。欢迎一起交流,也欢迎在留言板)或邮件给我提供相关资料,更欢迎提出批评指正~ 一、论文解读 基于强化学习的开创性工作 Neural Architecture Search with Reinforcement Learning Designing Neural Network Architectures Using Reinforcement Learning 基于遗传算法的开创性工作 Large-Scale Evolution of Image Classifiers Finding Better Topologies for Deep Convolutional Neural Network by Evolution 基于层或块的搜索 Learning Transferable Architectures for Scalable Image Recognition Practical Block-wise Neural Network Architecture Generation 权值共享的搜索 Efficient Neural Architecture Search via Parameter Sharing You Only Search Once: Single Shot Neural Architecture Search via Direct Sparse Optimization 可微分方法 DARTS:Differentiable Architecture Search 基于代理模型 Progressive Neural Architecture Search 基于one-shot模型 SMASH: One-Shot Model Architecture Search through HyperNetworks Understanding and Simplifying One-Shot Architecture Search Single Path One-Shot Neural Architecture Search with Uniform Sampling ScarletNAS: Bridging the Gap Between Scalability and Fairness in Neural Architecture Search 二、文献汇总 不定期更新NAS相关的文献: 综述文献 NAS文献 自动数据增强 三、相关网站、博客、资源 一本关于AutoML的书:AUTOML: METHODS, SYSTEMS, CHALLENGES 谷歌的Barret Zoph在ICCV2019做的报告:Neural Architecture Search and Beyond https://github.com/hibayesian/awesome-automl-papers github上关于自动化机器学习的资源汇总,NAS只是其中一部分 https://github.com/D-X-Y/Awesome-NAS 一个论文与代码汇总的github项目,都是发表在各届顶会上的论文 https://www.automl.org/automl/literature-on-neural-architecture-search/ 一个汇总最新论文的网址(灰常灰常多的论文),也包含arxiv上的论文 ICCV2019收录的NAS文章"},{"title":"about","date":"2020-06-03T16:48:00.000Z","updated":"2021-11-23T13:59:04.000Z","comments":true,"path":"about/index.html","permalink":"http://yoursite.com/about/index.html","excerpt":"","text":"感谢关注Jarvis’ Cabin!一个手工智能(AI)小作坊,您的到来令小站蓬荜生辉~ 博主就是这个小作坊的创建者,本小作坊主要生产机器学习相关的文章,多数为原创,也有汇总类的,也欢迎大家的投稿~鼠标移到头像下面的微信图标,即可显示博主的二维码,扫一扫即可加博主(如果图加载不出来请搜索微信号:wuan3076),欢迎一起交流~还有个交流群,想入群的话备注“入群”即可~本站在CSDN也有个镜像博客,基本是同步的,欢迎点这里访问~ 关于博主: 2012-2016:在美丽的冰城哈尔滨读本科,度过了最难忘的四年时光。专业为万金油的自动化专业,学过数电模电,学过控制理论,学过电力电子,学过自控原件,搞过电赛,搞过建模,焊过电路板,写过代码,真正的什么都会点但什么又都不会 2016-2021:在帝都北京开启博士生的生涯,称为一个苦逼的科研党。第一年方向摇摆不定,后来确定为深度学习与PHM故障诊断方向。完全非科班,坑一点一点踩,经验一点一点积累,最终感觉还是实习成长得最最快。期间两大研究主题:迁移学习和AutoDL,都是和故障诊断结合的,颇有点自动化什么都会点但又不精的感觉。路漫漫其修远兮,深知差距,还需不断提升内力 2021-至今:有幸加入京东,成为DMT的一员,新的征程已开启 小站建设历程: 2019年2月:诞生 2019年3月:增加PHM专栏 2019年11月:增加NAS专栏 2020年6月:改版主题(访问量已经重置了,嘤嘤嘤,之前应该已经突破8k),绑定域名:lixudong.ink 2020年6月:域名完成备案,添加CDN加速 2021年10月:增加“学术主页”栏目 2021年11月:增加“书单”栏目,欢迎爱读书的小伙伴们一起交流"},{"title":"PHM专栏","date":"2019-03-04T07:57:33.000Z","updated":"2020-06-04T06:18:10.000Z","comments":true,"path":"PHM专栏/index.html","permalink":"http://yoursite.com/PHM专栏/index.html","excerpt":"","text":"PHM是Prognostic and Health Management 的缩写,即故障预测与健康管理。PHM广泛应用于各个领域。是综合利用现代信息技术、人工智能技术的最新研究成果而提出的一种全新的管理健康状态的解决方案。PHM系统未来一段时间内系统失效可能性以及采取适当维护措施的能力,一般具备故障检测与隔离、故障诊断、故障预测、健康管理和部件寿命追踪等能力。近年来随着大数据和人工智能算法的兴起,PHM中数据驱动方法以及人工智能在其中的应用越来越受关注。开辟此专栏是为了汇总人工智能算法(主要是传统的机器学习算法和深度学习算法)在PHM中应用的相关学术资源,会不定期更新,持续跟踪相关领域的最新研究成果。也欢迎大家在评论区推荐资源! 一、最新文章 综述文献 普通神经网络与无监督方法 CNN故障诊断 迁移学习 二、相关数据集 此部分转载自:https://blog.csdn.net/hustcxl/article/details/89394428 1、CWRU(凯斯西储大学轴承数据中心) 数据集下载:https://csegroups.case.edu/bearingdatacenter/pages/welcome-case-western-reserve-university-bearing-data-center-website CWRU数据集是使用最为广泛的,文献较多。不一一举例。其中University of New South Wales 的Wade A. Smith在2015年进行了比较全面的总结和对比[1]。比较客观的综述和分析了使用数据进行诊断和分析研究的情况。官方网站提供的是.mat格式的数据,MATLAB直接使用比较方便。 Github上有人分享了在python中自动下载和使用的方法。https://github.com/Litchiware/cwru R语言中使用的方法:https://github.com/coldfir3/bearing_fault_analysis 2、MFPT(机械故障预防技术学会) 数据下载:https://mfpt.org/fault-data-sets/ 声学和振动数据库链接http://data-acoustics.com/measurements/bearing-faults/bearing-2/ 使用该数据集的相比于CWRU少一些,2012年更新。一些对数据描述的论文[2] MATLAB 文档关于MFPT轴承数据的故障诊断举例。https://ww2.mathworks.cn/help/predmaint/examples/Rolling-Element-Bearing-Fault-Diagnosis.html 3、德国Paderborn大学 数据下载:https://mb.uni-paderborn.de/kat/forschung/datacenter/bearing-datacenter/ 相关说明及论文[3, 4] 4、FEMTO-ST轴承数据集 由FEMTO-ST研究所建立的PHM IEEE 2012数据挑战期间使用的数据集[5-7] FEMTO-ST网站:https://www.femto-st.fr/en github链接:https://github.com/wkzs111/phm-ieee-2012-data-challenge-dataset 5、辛辛那提IMS 数据下载:https://ti.arc.nasa.gov/tech/dash/groups/pcoe/prognostic-data-repository/ 相关论文[8, 9] 6、XJTU-SY Bearing Datasets(西安交通大学 轴承数据集) 由西安交通大学雷亚国课题组王彪博士整理。数据集下载:http://biaowang.tech/xjtu-sy-bearing-datasets/ 使用数据集的论文[10] 7、东南大学 由东南大学严如强团队博士生邵思雨完成[12]。数据下载:https://github.com/cathysiyu/Mechanical-datasets 相关文献[11] 8、Acoustics and Vibration Database(振动与声学数据库) 提供一个手机振动故障数据集的公益性网站链接:http://data-acoustics.com/ 参考文献[1] Smith W A, Randall R B. Rolling element bearing diagnostics using the Case Western Reserve University data: A benchmark study[J]. Mechanical Systems and Signal Processing, 2015,64-65:100-131. [2] Lee D, Siu V, Cruz R, et al. Convolutional neural net and bearing fault analysis[C]//Proceedings of the International Conference on Data Mining (DMIN). The Steering Committee of The World Congress in Computer Science, Computer Engineering and Applied Computing (WorldComp), 2016: 194. [3] Bin Hasan M. Current based condition monitoring of electromechanical systems. Model-free drive system current monitoring: faults detection and diagnosis through statistical features extraction and support vector machines classification.[D]. University of Bradford, 2013. [4] Lessmeier C, Kimotho J K, Zimmer D, et al. Condition monitoring of bearing damage in electromechanical drive systems by using motor current signals of electric motors: a benchmark data set for data-driven classification: Proceedings of the European conference of the prognostics and health management society, 2016[C]. [5] Porotsky S, Bluvband Z. Remaining useful life estimation for systems with non-trendability behaviour: Prognostics & Health Management, 2012[C]. [6] Nectoux P, Gouriveau R, Medjaher K, et al. PRONOSTIA: An experimental platform for bearings accelerated degradation tests.: IEEE International Conference on Prognostics and Health Management, PHM’12., 2012[C]. IEEE Catalog Number: CPF12PHM-CDR. [7] E. S, H. O, A. S S V, et al. Estimation of remaining useful life of ball bearings using data driven methodologies: 2012 IEEE Conference on Prognostics and Health Management, 2012[C].2012 [8] Gousseau W, Antoni J, Girardin F, et al. Analysis of the Rolling Element Bearing data set of the Center for Intelligent Maintenance Systems of the University of Cincinnati: CM2016, 2016[C]. [9] Qiu H, Lee J, Lin J, et al. Wavelet filter-based weak signature detection method and its application on rolling element bearing prognostics[J]. Journal of Sound and Vibration, 2006,289(4):1066-1090. [10] B. W, Y. L, N. L, et al. A Hybrid Prognostics Approach for Estimating Remaining Useful Life of Rolling Element Bearings[J]. IEEE Transactions on Reliability, 2018:1-12. [11] Siyu S , Stephen M A , Ruqiang Y , et al. Highly-Accurate Machine Fault Diagnosis Using Deep Transfer Learning[J]. IEEE Transactions on Industrial Informatics, 2018:1-1."},{"title":"projects_show","date":"2020-06-25T03:23:10.000Z","updated":"2020-06-25T03:23:12.000Z","comments":true,"path":"projects-show/index.html","permalink":"http://yoursite.com/projects-show/index.html","excerpt":"","text":""},{"title":"数据结构与算法","date":"2020-08-03T07:08:10.000Z","updated":"2020-08-03T10:28:40.000Z","comments":true,"path":"数据结构与算法/index.html","permalink":"http://yoursite.com/数据结构与算法/index.html","excerpt":"","text":"此专栏汇集数据结构、算法方面的文章,主要包括基础知识讲解和leetcode题解。个人水平有限,如有错误欢迎在留言板提出~ 算法复杂度 链表 二分查找 哈希 递归 队与栈 优先队列 二叉树 平衡二叉树 二叉搜索树 动态规划 广度与深度优先搜索 位运算 并查集 LRU缓存 字典树"},{"title":"文献专栏","date":"2020-02-26T03:31:48.000Z","updated":"2020-03-11T14:53:00.000Z","comments":true,"path":"文献专栏/index.html","permalink":"http://yoursite.com/文献专栏/index.html","excerpt":"","text":"为了规范文献管理与阅读记录,特设此专栏来记录下载过的文献、阅读情况、基本内容、引用格式等。为了排版、记录方便以提高效率,所有记录放在CSDN专栏(https://blog.csdn.net/u014157632/category_9760481.html),下面给出每个类别的地址,欢迎访问,不定期更新~ 总目录:https://blog.csdn.net/u014157632/article/details/104578738AutoML 自动数据增强: 综述: ICCV2019相关NAS论文 NAS相关: PHM CNN方法-故障诊断 RNN方法-故障诊断 普通神经网络、自编码机 迁移学习 数据集相关文献 寿命预测 综述文献 迁移学习 迁移学习方法 相关理论 综述文献"},{"title":"机器学习wiki","date":"2020-07-31T07:52:35.000Z","updated":"2020-08-03T07:20:28.000Z","comments":true,"path":"机器学习wiki/index.html","permalink":"http://yoursite.com/机器学习wiki/index.html","excerpt":"","text":"机器学习wiki 面向深入学习机器学习知识的百科全书 此部分旨在整合机器学习的各个领域,涉及的内容广泛且较为深入,争取打造最全的机器学习百科全书,读者可以系统地学习也可作为工具书参考。机器学习wiki项目希望包含如下内容:机器学习的基础知识讲解(数学基础、常用模型、深度学习等)、机器学习应用(计算机视觉、语音、推荐、自动驾驶等)、基础编程实践(pytorch、tensorflow)等,为开源合作项目,仍在规划中,敬请期待,有意参与者请联系站长~ wiki目录 数学基础 概率论 矩阵论 经典机器学习算法 机器学习基础——概论 机器学习基础——模型评估与选择 线性回归 逻辑回归 朴素贝叶斯分类 支持向量机 决策树 模型集成 聚类 降维 强化学习 概率图模型 深度学习 深度学习概述 多层感知机 反向传播算法 卷积神经网络 循环神经网络 自编码机 生成式对抗网络 图卷积神经网络 深度强化学习 移动端模型 MobileNet 应用 计算机视觉 自然语言处理 语音识别 自动驾驶 推荐系统 计算广告 自动化机器学习 AutoML NAS 迁移学习 迁移学习理论 实战 Python技巧 numpy基础 pandas基础 sklearn基础 pytorch基础 房价预测 图像识别 目标检测 VAE/GAN生成图片 超分辨率重建 自动写诗 语音识别 语音分离 推荐算法 CTR预估 NAS 机器学习基础知识手册 面向机器学习面试的基础知识点总结 《机器学习基础知识手册》以pdf文档的形式开源,内容主要为基础知识的总结性整理,比较适用于准备面试。主要包含以下内容: 机器学习基础 数学基础 贝叶斯分类 线性回归 Logistic回归 支持向量机SVM 集成学习 树模型 降维 聚类 强化学习 深度学习 优化 由于水平有限以及时间紧张,本手册无法将机器学习的方方面面都涵盖到,对于一些常见机器学习问题肯定还有所遗漏,错误也在所难免。欢迎大家再issues里提出批评与建议、指正错误,也非常欢迎大家为手册扩成内容! 【注】手册里参考了很多网上的内容,在参考文献里附有链接,如果有内容未附上链接或者侵权,请联系作者,谢谢~ 下载地址: 百度网盘链接:https://pan.baidu.com/s/1cUT-E0guPen20PO5OVjZoQ提取码:pbjd"},{"title":"资源文档下载","date":"2019-03-08T03:12:15.000Z","updated":"2019-03-29T11:28:58.000Z","comments":true,"path":"资源文档下载/index.html","permalink":"http://yoursite.com/资源文档下载/index.html","excerpt":"","text":"更新时间:2019年3月28日 电子书下载 《Learning scikit-learn Machine Learning in Python》:Experience the benefits of machine learning techniques by applying them to real-world problems using Python and the open source scikit-learn library 《统计学习理论的本质》:本书介绍了统计学习理论和支持向量机的关键思想、结论和方法,以及该领域的最新进展。统计学习理论是针对小样本情况研究统计学习规律的理论,是传统统计学的重要发展和补充。其核心思想是通过控制学习机器的容量实现对推广能力的控制。 文档下载 《troubleshooting-deep-neural-networks-01-19》:已经学会深度学习,但你搭建的模型为什么还跑不动,到底哪里出了问题?看懂了教材,一到编程调试就跪,为了寻找bug的你是否曾经手足无措?虽然网络上深度学习的教材很多,但是手把手教你调试的技巧却不常见。一位来自伯克利的小哥Josh Robin分享了他的深度学习debug心得,从最简单模型开始一步步深入到复杂模型,希望能给刚上手的你一点帮助。 《斯坦福统计学习理论笔记》:斯坦福大学计算机系教授Percy Liang整理的一个统计学习理论笔记。非常推荐,可以当做之前吴恩达老师机器学习课程的一个补充。 《pandas练习题notebook》:网上可以搜到大量的 pandas 教程和官方文档,但没有简单的方法来练习。教程是很好的资源,但要付诸实践。 只有实践,才能更好的加深学习。原项目地址 网站资源 http://transferlearning.xyz/:非常全的迁移学习资料汇总网站 https://www.kaggle.com/shivamb/data-science-glossary-on-kaggle/notebook:kaggle各类比赛中人气最高的kernel汇总,按回归算法、正则化算法、树模型、深度学习、聚类算法、度量算法、数据处理、降维、集成、文本数据、数据科学工具、数据可视化、时间序列等归类 http://www.chioka.in/kaggle-competition-solutions/:各个kaggle比赛中前几名的解决方案"}],"posts":[{"title":"数据结构与算法——优先队列","slug":"数据结构与算法——优先队列","date":"2020-06-04T07:19:12.000Z","updated":"2020-06-04T07:20:02.000Z","comments":true,"path":"2020/06/04/数据结构与算法——优先队列/","link":"","permalink":"http://yoursite.com/2020/06/04/数据结构与算法——优先队列/","excerpt":"","text":"一、优先队列优先队列按照队列的方式正常入队,但按照优先级出队。有两种实现方式:堆(二插堆、多项式堆等等)和二叉搜索树。这里重点讲解二叉堆,关于二叉搜索树的内容见这篇文章。 堆是一种特殊的完全二叉树。大根堆: 完全二叉树的任一节点都比其孩子节点大。小根堆: 完全二叉树的任一节点都比其孩子节点小。堆的向下调整: 假设根节点的左右子树都是堆,但根节点不满足堆的性质,可以通过一次向下调整将其变成一个堆。因为二叉堆是完全二叉树,一般可以用数组来表示,这样不会浪费空间。大根堆和小根堆的举例如下所示:用数组表示的二叉堆,有如下性质: 第$i$个节点的左孩子的下标为$2i+1$,右孩子的下标为$2i+2$。 第$i$个节点的父亲节点的下标为$(i-1)//2$ 二叉堆的时间复杂度: 插入操作:$O(\\log n)$ 删除操作:$O(\\log n)$ 查询最小值:$O(1)$ python实现:heapq import heapq # 将array列表转为堆的结构 heap.heapify(array) # 弹出堆中的最小值 heapq.heappop(array) # 往堆中插入新值a heapq.heappush(array, a) # 先进行heappop(array),再进行heappush(array, a)操作 heapq.heapreplace(array, a) # 获得array中前k个最大的值 heapq.nlargest(k, array) # 获得array中前k个最小的值 heapq.nsmallest(k, array) 二、例题1、合并k个有序链表此题为leetcode第23题合并 k 个排序链表,返回合并后的排序链表。 import heapq class Solution: def mergeKLists(self, lists: List[ListNode]) -> ListNode: head = p = ListNode(0) # 建立堆 a = [] for i in range(len(lists)): if lists[i] : # 元组在heapq里比较的机制是从元组首位0开始,即遇到相同,就比较元组下一位 # 比如(1,2), (1,3),前者比后者小。 # 这题刚好node值有重复的,同时ListNode无法被比较,所以会报错 heapq.heappush(a, (lists[i].val, i)) lists[i] = lists[i].next while a: val, idx = heapq.heappop(a) p.next = ListNode(val) p = p.next if lists[idx]: heapq.heappush(a, (lists[idx].val, idx)) lists[idx] = lists[idx].next return head.next 时间复杂度:$O(n \\log k)$ 空间复杂度:$O(k+n)$,最小堆需要k个空间,新链表需要n个空间 2、数据流中的第k大元素此题为leetcode第703题设计一个找到数据流中第K大元素的类(class)。注意是排序后的第K大元素,不是第K个不同的元素。你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器,它包含数据流中的初始元素。每次调用 KthLargest.add,返回当前数据流中第K大的元素。 import heapq class KthLargest: def __init__(self, k: int, nums: List[int]): self.nums = nums self.k = k heapq.heapify(self.nums) # 留下k个元素,即前k大的 while len(self.nums) > k: heapq.heappop(self.nums) def add(self, val: int) -> int: if len(self.nums) < self.k: heapq.heappush(self.nums, val) elif self.nums[0] < val: # 新的值更大则更新 heapq.heapreplace(self.nums, val) return self.nums[0]","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"算法","slug":"算法","permalink":"http://yoursite.com/tags/算法/"},{"name":"数据结构","slug":"数据结构","permalink":"http://yoursite.com/tags/数据结构/"}]},{"title":"数据结构与算法——并查集","slug":"数据结构与算法——并查集","date":"2020-06-04T07:18:27.000Z","updated":"2020-06-04T07:18:56.000Z","comments":true,"path":"2020/06/04/数据结构与算法——并查集/","link":"","permalink":"http://yoursite.com/2020/06/04/数据结构与算法——并查集/","excerpt":"","text":"一、并查集并查集(Union & find) 是一种树形的数据结构,用于处理一些不交集(Disjoint sets)的合并与查询的问题。初始化时把每个点所在集合初始化为其自身。Find: 确定元素属于哪一个子集,它可以被用来确定两个元素是否属于同一子集。Union: 将两个子集合并成同一个子集。 如下图所示,一开始有7个字母,每个都指向自己:根据某种规则,将相关的字母合并起来,即某个字母会指向另一个字母。假设合并之后是下面的样子: 上图是个树形的结构,左边的集合(a)的根节点为a,它的深度为2,这里树的深度我们称之为秩(rank)。对于这样的树结构,我们如果要合并两个树,可以将秩低的树合并到秩高的树,这样不会增加整个树的秩,如下图所示: 对于并查集,还有一种优化的方式,即路径压缩。我们希望每个节点到根节点的路径尽可能地短,可以将每个节点的父节点设为根节点,比如(a)可以压缩为: 二、例题(1)岛屿数量此题为leetcode第200题此题可以用DFS或BFS解,这两种解法点这里。下面我们用并查集的方法解题。 class UnionFind(object): def __init__(self, grid): m, n = len(grid), len(grid[0]) self.count = 0 self.parent = [-1] * (m * n) # 一维数组表示并查集 self.rank = [0] * (m * n) # 初始化,为1的格子指向自己 for i in range(m): for j in range(n): if grid[i][j] == 1: self.parent[i * n + j] = i * n + j self.count += 1 # 找根节点 def find(self, i): if self.parent[i] != i: self.parent[i] = self.find(self.parent[i]) return self.parent[i] # 合并 def union(self, x, y): rootx = self.find(x) rooty = self.find(y) if rootx != rooty: if self.rank[rootx] > self.rank[rooty]: # 低秩合并到高秩 self.parent[rooty] = rootx elif self.rank[rootx] < self.rank[rooty]: self.parent[rootx] = rooty else: self.parent[rooty] = rootx self.rank[rootx] += 1 self.count -= 1 class Solution: def numIslands(self, grid: List[List[str]]) -> int: if not grid or len(grid[0]) == 0: return 0 grid = [[int(i) for i in a] for a in grid] # str-->int directions = [(-1, 0), (0, -1), (1, 0), (0, 1)] uf = UnionFind(grid) # 实例化并查集 m, n = len(grid), len(grid[0]) # 遍历每个元素 for i in range(m): for j in range(n): if grid[i][j] == 0: continue # 遍历4个方向 for dx, dy in directions: ii, jj = i + dx, j + dy # 如果合法的话就合并 if 0 <= ii < m and 0 <= jj < n and grid[ii][jj] == 1: uf.union(i * n + j, ii * n + jj) return uf.count (2)朋友圈此题为leetcode第547题班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。 class Solution: def findCircleNum(self, M: List[List[int]]) -> int: if len(M) < 2: return len(M) if len(M) == 2: if M[0][1] == 1: return 1 else: return 2 n = len(M) uf = UnionFind(M) # 只需遍历右上三角即可(不包括对角线) for i in range(n-1): for j in range(i+1, n): if M[i][j] == 1: uf.union(i, j) return uf.count # 并查集 class UnionFind(object): def __init__(self, M): n = len(M) self.count = 0 self.parent = [-1] * n # 一维数组表示并查集 self.rank = [0] * n # 初始化,为1的格子指向自己 for i in range(n): self.parent[i] = i self.count += 1 # 找根节点 def find(self, i): if self.parent[i] != i: self.parent[i] = self.find(self.parent[i]) return self.parent[i] # 合并 def union(self, x, y): rootx = self.find(x) rooty = self.find(y) if rootx != rooty: if self.rank[rootx] > self.rank[rooty]: # 低秩合并到高秩 self.parent[rooty] = rootx elif self.rank[rootx] < self.rank[rooty]: self.parent[rootx] = rooty else: self.parent[rooty] = rootx self.rank[rootx] += 1 self.count -= 1","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"算法","slug":"算法","permalink":"http://yoursite.com/tags/算法/"},{"name":"数据结构","slug":"数据结构","permalink":"http://yoursite.com/tags/数据结构/"}]},{"title":"数据结构与算法——LRU缓存","slug":"数据结构与算法——LRU缓存","date":"2020-06-04T07:17:40.000Z","updated":"2020-06-04T07:18:12.000Z","comments":true,"path":"2020/06/04/数据结构与算法——LRU缓存/","link":"","permalink":"http://yoursite.com/2020/06/04/数据结构与算法——LRU缓存/","excerpt":"","text":"一、LRU缓存LRU(least recently used)最近最少使用缓存机制,在计算机的缓存满时,会最先淘汰近期最少使用的数据。示意图如下图所示:设缓存的大小为5,在缓存未满之前,ABCDEF依次进入缓存。当要缓存F时,A近期没有被使用,因此淘汰掉,F放到头的位置,剩下的往后挪。当再次进来C的时候,因为缓存里已经有C了,因此把C提到缓存的头来。再进来G的时候,G放到头,剩下的往后挪。 二、实现LRU缓存此题为leetcode第146题运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。 获取数据 get(key) :如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 写入数据 put(key, value) - 如果密钥已经存在,则变更其数据值;如果密钥不存在,则插入该组「密钥/数据值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。 # 有序字典 import collections class LRUCache: def __init__(self, capacity: int): self.dic = collections.OrderedDict() self.remain = capacity def get(self, key: int) -> int: if key not in self.dic: return -1 v = self.dic.pop(key) # 获取指定key的value,并在字典中删除 self.dic[key] = v # 将key作为最新的一个 return v def put(self, key: int, value: int) -> None: if key in self.dic: self.dic.pop(key) else: if self.remain > 0: self.remain -= 1 else: self.dic.popitem(last=False) # last为False时,删除最先放进来的键值对对并返回该键值对 self.dic[key] = value","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"算法","slug":"算法","permalink":"http://yoursite.com/tags/算法/"},{"name":"数据结构","slug":"数据结构","permalink":"http://yoursite.com/tags/数据结构/"}]},{"title":"数据结构与算法——字典树","slug":"数据结构与算法——字典树","date":"2020-06-04T07:16:31.000Z","updated":"2020-06-04T07:17:18.000Z","comments":true,"path":"2020/06/04/数据结构与算法——字典树/","link":"","permalink":"http://yoursite.com/2020/06/04/数据结构与算法——字典树/","excerpt":"","text":"一、字典树字典树(Trie) 又称单词查找树或键树,是一种哈希树的变种。典型的应用是用于统计和排序大量的字符串(但不限于字符串),优点是可以可以最大限度地减少无畏的字符串比较,查询效率比哈希表高。Trie的核心思想是空间换时间,利用字符串的公共前缀来降低查询的时间。 比如有一个包含多个单词的列表:[‘word’, ‘work’, ‘code’, ‘coffe’],可以下面的字典树表示: graph TD root((root)) -- w --> w(( )) w -- o --> o(( )) o -- r --> r(( )) r -- d --> d(( )) r -- k --> k(( )) root -- c --> c(( )) c -- o --> o2(( )) o2 -- d --> d2(( )) d2 -- e --> e(( )) o2 -- f --> f(( )) f -- f --> f2(( )) f2 -- e --> e2(( )) style root fill:#f9f style d fill:#f9f style k fill:#f9f style e fill:#f9f style e2 fill:#f9f 字典树的性质: 根节点不包含字符,除根节点外每个节点只包含一个字符 从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串 每个节点的所有子节点包含的字符串都不同 从头实现字符串(leetcode第208题):实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。示例: Trie trie = new Trie(); trie.insert(\"apple\"); trie.search(\"apple\"); // 返回 true trie.search(\"app\"); // 返回 false trie.startsWith(\"app\"); // 返回 true trie.insert(\"app\"); trie.search(\"app\"); // 返回 true 说明:你可以假设所有的输入都是由小写字母 a-z 构成的;保证所有输入均为非空字符串。 class Trie: def __init__(self): \"\"\" Initialize your data structure here. \"\"\" self.root = {} self.end_of_word = '#' def insert(self, word: str) -> None: \"\"\" Inserts a word into the trie. \"\"\" node = self.root for c in word: node = node.setdefault(c, {}) node[self.end_of_word] = 1 def search(self, word: str) -> bool: \"\"\" Returns if the word is in the trie. \"\"\" node = self.root for c in word: if c not in node: return False node = node[c] return self.end_of_word in node def startsWith(self, prefix: str) -> bool: \"\"\" Returns if there is any word in the trie that starts with the given prefix. \"\"\" node = self.root for c in prefix: if c not in node: return False node = node[c] return True 上面是比较造轮子的一种方法,而在python里可以直接用字典来实现字典树 二、例题(1)单词搜索II此题为leetcode第212题给定一个二维网格 board 和一个字典中的单词列表 words,找出所有同时在二维网格和字典中出现的单词。单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。 思路:字典树+深度优先搜索 # 方向数组 dx = [-1, 1, 0, 0] dy = [0, 0, -1, 1] # word结束标记 end_of_word = '#' class Solution: def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: if not board or len(board[0]) == 0 or len(words) == 0: return [] self.res = set() # 结果保存在集合中 root = {} # 初始化根节点为空字典 # 对列表里的word构建字典树 for word in words: node = root # 这里的root将会是一个字典层层嵌套的结构。 # 如果c在node里,则取出键为c的值,如果不在,则在键c下新建空字典 for c in word: node = node.setdefault(c, {}) node[end_of_word] = end_of_word # 标记word已结束 self.m, self.n = len(board), len(board[0]) # 遍历board,进行DFS for i in range(self.m): for j in range(self.n): if board[i][j] in root: self._dfs(board, i, j, '', root) return list(self.res) def _dfs(self, board, i, j, curr_word, curr_dict): curr_word += board[i][j] curr_dict = curr_dict[board[i][j]] if end_of_word in curr_dict: self.res.add(curr_word) temp, board[i][j] = board[i][j], '*' # '*'表示暂时标记为已访问过 # 在(i, j)的4个方向上遍历 for k in range(4): x, y = i + dx[k], j + dy[k] # 如果x、y没有越界,并且没有被访问过,并且当前字母在curr_dict中 if 0 <= x < self.m and 0 <= y < self.n and board[x][y] != '*' and board[x][y] in curr_dict: self._dfs(board, x, y, curr_word, curr_dict) board[i][j] = temp # 恢复board[i][j]的值","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"算法","slug":"算法","permalink":"http://yoursite.com/tags/算法/"},{"name":"数据结构","slug":"数据结构","permalink":"http://yoursite.com/tags/数据结构/"}]},{"title":"数据结构与算法——二分查找","slug":"数据结构与算法——二分查找","date":"2020-06-04T07:15:39.000Z","updated":"2020-06-04T07:16:14.000Z","comments":true,"path":"2020/06/04/数据结构与算法——二分查找/","link":"","permalink":"http://yoursite.com/2020/06/04/数据结构与算法——二分查找/","excerpt":"","text":"一、二分查找使用二分查找的条件: 单调递增或递减 存在上下界 能够通过索引访问(数组更适合二分查找,链表不适合) 程序模板: left, right = 0, len(array) while left <= right: mid = left + (right - left) // 2 if array[mid] == target: return True elif array[mid] < target: left = mid + 1 else: right = mid -1 二、例题(1)sqrt(x)此题为leetcode第69题实现 int sqrt(int x) 函数。计算并返回 x 的平方根,其中 x 是非负整数。由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。 class Solution: def mySqrt(self, x: int) -> int: if x == 0 or x == 1: return x left, right = 2, x // 2 while left <= right: mid = left + (right - left) // 2 if mid * mid == x: return mid elif mid * mid < x: left = mid + 1 else: right = mid - 1 return right","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"算法","slug":"算法","permalink":"http://yoursite.com/tags/算法/"},{"name":"数据结构","slug":"数据结构","permalink":"http://yoursite.com/tags/数据结构/"}]},{"title":"数据结构与算法——十大排序算法","slug":"数据结构与算法——十大排序算法","date":"2020-06-04T07:13:43.000Z","updated":"2020-06-04T07:14:34.000Z","comments":true,"path":"2020/06/04/数据结构与算法——十大排序算法/","link":"","permalink":"http://yoursite.com/2020/06/04/数据结构与算法——十大排序算法/","excerpt":"","text":"一、冒泡排序排序过程: 列表每两个相邻的数,如果前者大于后者,则交换这两个数;遍历列表,完成一趟排序 继续从头遍历,重复上述过程,直到没有发生交换为止def BubbleSort(a): if len(a) == 0: return None for i in range(len(a) - 1): exchange = False # 标志位,第i趟如果没有发生交换,则排序已经完成,不需要再进行后面的冒泡 for j in range(len(a) - i - 1): if a[j] > a[j + 1]: a[j], a[j + 1] = a[j + 1], a[j] exchange = True if exchange is False: # 没有发生交换,返回 return 复杂度分析: 最好情况:$O(n)$ 最坏情况:$O(n^2)$ 平均情况:$O(n^2)$ 二、选择排序排序过程: 一趟遍历记录最小的数,放到一个位置 再遍历一趟,记录无需去最小的数,放到有序区的第二个位置 重复以上过程,直到列表结束 def SelectionSort(a): if len(a) == 0: return None for i in range(len(a) - 1): min_index = i # 记录无序区最小数的位置 for j in range(i, len(a)): if a[j] < a[min_index]: min_index = j if min_index != i: a[i], a[min_index] = a[min_index], a[i] 复杂度分析: 最好情况:$O(n^2)$ 最坏情况:$O(n^2)$ 平均情况:$O(n^2)$ 三、插入排序排序过程: 步骤1:从第一个元素$a[i], i=0$开始,该元素为有序区 步骤2:取下一个元素$a[i+1]$,在有序区的元素序列中从后向前扫描 步骤3:如果有序区元素$a[j]$大于新元素$a[i+1]$,将该元素移到下一位置$a[j-1]$; 步骤4:重复步骤3,直到找到有序区的元素小于或者等于新元素的位置; 步骤5:将新元素$a[i+1]$插入到该位置后; 步骤6:重复步骤2~5 def InsertionSort(a): if len(a) == 0: return None for i in range(1, len(a)): temp = a[i] # 取无序区的第一个数 j = i - 1 # 有序区的倒数第一个数索引为i-1 while j >= 0 and temp < a[j]: # 遍历有序区 a[j + 1] = a[j] j -= 1 a[j + 1] = temp # 找到temp的位置 复杂度分析: 最好情况:$O(n)$ 最坏情况:$O(n^2)$ 平均情况:$O(n^2)$ 四、快速排序排序过程: 取一个元素(一般为第一个元素)P,使元素归位 归位操作后的列表被P分为两部分,P左边的元素都比P小,右边的都比P大 递归地把小于P的子数列和大于P的子数列排序 def partition(a, left, right): if len(a) == 0: return None temp = a[left] while left < right: # 从右边找比temp小的数 while left < right and a[right] >= temp: right -= 1 a[left] = a[right] # 从左边找比temp大的数 while left < right and a[left] <= temp: left += 1 a[right] = a[left] # 找到了temp的位置 a[left] = temp # 返回temp1的位置, return right也可以,因为left和right重合 return left def QuickSort(a, left, right): if left < right: # 选取a的第一个元素P,归位 mid = partition(a, left, right) # 递归P的左边 QuickSort(a, left, mid - 1) # 递归P的右边 QuickSort(a, mid + 1, right) 复杂度分析: 最佳情况:$O(n \\log n)$ 最差情况:$O(n^2)$ 平均情况:$O(n \\log n)$五、堆排序堆是一种特殊的完全二叉树。大根堆: 完全二叉树的任一节点都比其孩子节点大。小根堆: 完全二叉树的任一节点都比其孩子节点小。 堆的向下调整: 假设根节点的左右子树都是堆,但根节点不满足堆的性质,可以通过一次向下调整将其变成一个堆。 排序过程: 步骤1:建立大根堆 步骤2:得到对顶元素,为最大元素 步骤3:堆顶元素和堆的最后一个元素交换,交换后可以通过一次调整使堆有序(不包括刚才及之前被换到堆后面的元素) 步骤4:堆顶元素为第二大元素,重复步骤3,直到堆变空 def adjustHeap(a, low, high): i = low # 指向根节点 j = 2 * i + 1 # 指向根节点的左孩子 temp = a[i] while j <= high: # 如果有右孩子,并且右孩子比左孩子大 if j + 1 <= high and a[j+1] > a[j]: j += 1 # 指向右孩子 # 如果子节点比temp大,交换a[i]和a[j] if a[j] > temp: a[i] = a[j] i = j j = 2 * i + 1 else: break a[i] = temp def HeapSort(a): if len(a) == 0: return None n = len(a) # 从底向上地建立大根堆,从最后一个非叶子节点开始,即(n-2)//2 for i in range((n - 2) // 2, -1, -1): adjustHeap(a, i, n-1) # 从最后一个元素开始,与堆顶调换 for i in range(n-1, -1, -1): a[0], a[i] = a[i], a[0] # 堆顶与a[i]调换 adjustHeap(a, 0, i-1) 复杂度分析: 最佳情况:$O(n \\log n)$ 最差情况:$O(n \\log n)$ 平均情况:$O(n \\log n)$ python里堆的内置模块:heapq # 堆的内置模块 import heapq a = list(range(100)) random.shuffle(a) heapq.heapify(a) # 建堆 # 依次出数 for i in range(len(a)): print(heapq.heappop(a), end=',') top-k问题: 有n个数,取前k大的数(k<n) 取列表前k个元素建立一个小根堆 从第k+1个开始依次向后遍历原列表,如果小于堆顶,则跳过改元素;如果大于堆顶,则将该元素放到堆顶,进行一次调整 遍历结束后,倒序弹出堆顶 # 调整小根堆 def adjustHeap(a, low, high): i = low # 指向根节点 j = 2 * i + 1 # 指向根节点的左孩子 temp = a[i] while j <= high: # 如果有右孩子,并且右孩子比左孩子小 if j + 1 <= high and a[j+1] < a[j]: j += 1 # 指向右孩子 # 如果子节点比temp小,交换a[i]和a[j] if a[j] < temp: a[i] = a[j] i = j j = 2 * i + 1 else: break a[i] = temp def top_k(a, k): if len(a) == 0: return None heap = a[:k] # 建堆 for i in range((k - 2) // 2, -1, -1): adjustHeap(heap, i, k-1) # 遍历 for i in range(k, len(a) - 1): if a[i] > heap[0]: heap[0] = a[i] adjustHeap(heap, 0, k-1) # 出数 for i in range(k - 1, -1, -1): heap[0], heap[i] = heap[i], heap[0] adjustHeap(heap, 0, i - 1) return heap 六、归并排序归并: 将两段有序列表合并成一个有序列表 排序过程: 把长度为n的输入列表分为长度为n//2的子序列 对这两个子序列分别采用归并排序 将两个排序好的子序列合并成一个最终的排序序列def merge(a, low, mid, high): # a的左右半边子序列已为有序 i, j = low, mid + 1 temp = [] # 依次比较 while i <= mid and j <= high: # 只要两个子序列还有数 if a[i] < a[j]: temp.append(a[i]) i += 1 else: temp.append(a[j]) j += 1 # 上面循环结束后至少有一个子序列已被完全遍历 # 下面将可能剩下的部分添加到temp temp.extend(a[i:mid+1]) temp.extend(a[j:high+1]) #print(len(a[i:mid+1]), len(a[j:high]), len(a[low:high+1]), len(temp)) # 替换原数组 a[low:high+1] = temp def MergeSort(a, low, high): if len(a) == 0: return None if low < high: # low为a的最左边,high为a的最右边,mid为左半边子序列的最右边 mid = (low + high) // 2 # 归并左半边子序列 MergeSort(a, low, mid) # 归并右半边子序列 MergeSort(a, mid+1, high) # 合并两个有序子序列 merge(a, low, mid, high) 复杂度分析: 最佳情况:$O(n)$ 最差情况:$O(n \\log n)$ 平均情况:$O(n \\log n)$ 七、希尔排序希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,同时该算法是冲破$O(n^2)$的第一批算法之一。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。希尔排序是把记录按下表的一定增量分组,对每组使用直接插入排序算法排序。 排序过程: 首先取一个整数$d_1= n // 2$,将列表分为$d_1$个组,每组相邻元素之间距离为$d_1$,在每组里进行插入排序 去第二个整数$d_2=d_1 // 2$,重复上述分组排序过程,直到$d_i=1$,即所有元素都在同一组内插入排序 希尔排序每趟使列表整体越来越接近有序 def InsertSortWithGap(a, gap): if len(a) == 0: return None for i in range(1, len(a)): temp = a[i] # 取无序区的第一个数 j = i - gap # 有序区的倒数第一个数索引为i-gap while j >= 0 and temp < a[j]: # 遍历有序区 a[j + gap] = a[j] j -= gap a[j + gap] = temp # 找到temp的位置 def ShellSort(a): if len(a) == 0: return None d = len(a) // 2 while d >= 1: InsertSortWithGap(a, d) d //= 2 复杂度分析: 希尔排序的复杂度分析是个难题,根据选取的分组整数序列$d_1, d_2, \\cdots, d_i$的不同,其时间复杂度也不同,有的复杂度由于数学上的难题仍然没有定论。对于我们介绍的一直除2的这种方式: 最佳情况:$O(n \\log ^2 n)$ 最坏情况:$O(n \\log ^2 n)$ 平均情况:$O(n \\log n)$ 八、计数排序基数排序要求已知列表中数的范围在0到$k$之间 排序过程: 步骤1:找出待排序的列表中最大和最小的元素; 步骤2:统计数组中每个值为$i$的元素出现的次数,存入数组$C$的第$i$项; 步骤3:遍历列表,对所有元素的计数累加; 步骤4:填充目标列表:将每个元素$i$放在新列表的第$C(i)$项,每放一个元素就将$C(i)$减去1。 def CountingSort(a): if len(a) == 0: return None # 找最大最小值 min_, max_ = a[0], a[0] for num in a: if num > max_: max_ = num if num < min_: min_ = num # 初始化计数数组 C = [0] * (max_ - min_ + 1) # 遍历数组 for num in a: C[num] += 1 # 填充原数组 a.clear() for i, c in enumerate(C): for j in range(c): a.append(i) 复杂度分析: 最佳情况:$O(n+k)$ 最差情况:$O(n+k)$ 平均情况:$O(n+k)$ 九、桶排序假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序 排序过程: 步骤1:人为设置一个bins,即有多少个桶。比如$[0,1, \\cdots, 9, 10]$这个列表,bins=5,那么第一个桶放0、1,第二个桶放2、3,第三个桶放4、5,依次类推; 步骤2:遍历列表,并且把元素一个一个放到对应的桶里去; 步骤3:对每个不是空的桶进行排序,可以使用其它排序方法,也可以递归使用桶排序; 步骤4:从不是空的桶里把排好序的数据拼接起来。def BucketSort(a, bins): if len(a) == 0: return None # 初始化桶 buckets = [[] for _ in range(bins)] # 找到最大值最小值 min_, max_ = a[0], a[0] for num in a: if num > max_: max_ = num if num < min_: min_ = num # 遍历 for num in a: i = min(num // (max_ // bins), bins-1) # 第几号桶 buckets[i].append(num) # 保持桶内顺序 for j in range(len(buckets[i]) - 1, 0,-1): if buckets[i][j] < buckets[i][j - 1]: # 桶内下小上大 buckets[i][j], buckets[i][j - 1] = buckets[i][j - 1], buckets[i][j] else: break # 从桶内拿出 sorted_a = [] for bucket in buckets: sorted_a.extend(bucket) return sorted_a 复杂度分析: 最佳情况:$O(n+k)$ 最差情况:$O(n^2)$ 平均情况:$O(n+k)$ 十、基数排序基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。 排序过程: 步骤1:取得数组中的最大数,并取得位数; 步骤2:取列表中元素的最低位,按最低位对元素进行分桶(10个桶,0-9); 步骤3:按桶的顺序还原列表,然后取元素的导数第二位,对元素再次分桶; 步骤4:重复上述分桶、还原的过程,直到最大元素的最高位 def RadixSort(a): if len(a) == 0: return None max_ = max(a) it = 0 while 10 ** it <= max_: # 按倒数第it位分桶 buckets = [[] for _ in range(10)] for num in a: digit = (num // 10 ** it) % 10 # 取倒数第it位数 buckets[digit].append(num) it += 1 # 出桶 a.clear() for bucket in buckets: a.extend(bucket) 复杂度分析: 最佳情况:$O(n \\times k)$ 最差情况:$O(n \\times k)$ 平均情况:$O(n \\times k)$ 十一、总结 排序算法 平均时间复杂度 最好情况 最坏情况 空间复杂度 排序方式 稳定 冒泡排序 $O(n^2)$ O(n) $O(n^2)$ $O(1)$ in-place 稳定 选择排序 $O(n^2)$ O(n^2) $O(n^2)$ $O(1)$ in-place 不稳定 插入排序 $O(n^2)$ O(n) $O(n^2)$ $O(1)$ in-place 稳定 快速排序 $O(n \\log n)$ $O(n \\log n)$ $O(n^2)$ $O(\\log n)$ in-place 不稳定 堆排序 $O(n \\log n)$ $O(n \\log n)$ $O(n \\log n)$ $O(1)$ in-place 不稳定 归并排序 $O(n \\log n)$ $O(n \\log n)$ $O(n \\log n)$ $O(n)$ out-place 稳定 希尔排序 $O(n \\log n)$ $O(n \\log^2 n)$ $O(n \\log^2 n)$ $O(1)$ in-place 不稳定 计数排序 $O(n+k)$ O(n+k) $O(n+k)$ $O(k)$ out-place 稳定 桶排序 $O(n+k)$ O(n+k) $O(n^2)$ $O(n+k)$ out-place 稳定 基数排序 $O(n \\times k)$ $O(n \\times k)$ $O(n \\times k)$ $O(n+k)$ out-place 稳定 上表中,in-place表示占用常数内存,不占额外内存(递归内存除外),out-place表示占用额外内存;$k$为桶的个数;稳定表示如果a原本在b前面且a==b,排序后a仍然在b前面,不稳定则是a可能出现在b后面。 参考文献[1] https://blog.csdn.net/weixin_41190227/article/details/86600821[2] https://edu.csdn.net/course/detail/24449","categories":[],"tags":[]},{"title":"数据结构与算法——动态规划","slug":"数据结构与算法——动态规划","date":"2020-06-04T07:12:33.000Z","updated":"2020-06-04T07:13:04.000Z","comments":true,"path":"2020/06/04/数据结构与算法——动态规划/","link":"","permalink":"http://yoursite.com/2020/06/04/数据结构与算法——动态规划/","excerpt":"","text":"一、动态规划1、从斐波那契数列说起斐波那契数列我们比较熟悉了,它的递推公式为$f(n)=f(n-1)+f(n-2)$,它的解法在《数据结构与算法——递归》这一节里也说过了。直接使用递归会造成很多重复计算,它的时间复杂度为$O(2^N)$;而使用记忆化,即将中间过程缓存起来,可以降到$O(N)$的时间复杂度。实际上这样的递归是“从上而下”的,我们可以“从下而上”地做,由$f(0)$和$f(1)$得到$f(2)$,再由$f(1)$和$f(2)$得到$f(3)$……这样的写法如下所示:F[0], F[1] = 0, 1 for i in range(2, n): F[i] = F[i-1] + F[i-2] 上面的“由下而上地使用递推公式”代表了动态规划的基本思想。上面的斐波那契数列的解法,是最最简单的动态规划,实际上的递推公式会更复杂,比如会有各种限制条件。 动态规划的基本思想:问题的最优解如果可以由子问题的最优解推导得到,则可以先求解子问题的最优解,再构造原问题的最优解;若子问题有较多的重复出现,则可以自底向上从最终子问题向原问题逐步求解。[1]状态:在动规解题中,我们将和子问题相关的各个变量的一组取值,称之为一个“状态”。[2]状态转移方程:即递推公式。上面的斐波那契数列的递推公式是给定的,而一般情况下需要我们自己推导出来递推公式。 2、举例进一步说明现在举个例子进一步说明动态规划:数路径问题。有下面一个$m$行$n$列的方格格,从最左上角的$(0, 0)$出发,达到最右下角的$(m-1, n-1)$,一共有几条路?图上颜色的格子是障碍物不能走,并且只能向右或向下走。 对于这个问题,我们可以递归地求解,如下图所示。从一开始,可以向右走到B,也可以向下走到A,那么从start到end的有多少条路径=从B到end的路径数+从A到end的路径数。同样从B到end的路径数=从E到end的路径数+从C到end的路径树,对于A也是类似的,这样可以一直递归下去。但是这样会有个问题,和斐波那契数列的原始递归解法一样,有很多路径被重复计算了,比如从C到end的路径,它的时间复杂度也会是指数级的。 根据上节对斐波那契数列的分析,我们可以采用“自底向上”的方式,如下图所示,我们从end开始往前推。要到达end,只有从红色箭头指的两个格子那里走(因为只能向右走或向下走)。也就是说,当处于这两个格子之一的时候,只有一条路可以走,我们在格子里记“1”。对于最下面一排的格子,我们都只能向右走,所以都标记为“1”。从蓝色箭头指的格子出发,我们可以向右走也可以向下走,有两条路所以标记为“2”。这个“2”其实是右边格子到end的路径数+下面格子到end的路径数,也就是“1+1”。同理,白色箭头指的格子应该标记为1+1=2,也有两条路可走;绿色箭头指的格子应该标记为2+1=3,则有3条路可走。 就这样从end开始一直往上遍历每个格子,我们可以把格子都标记上,一直到start。最后start右边是17,下边是10,因此这个题的结果就是27。 动态规划模板: # 以二维为例,只是一个大概的框架 def DP(): # 定义状态 dp = [[0 for i in range(m + 1)] for j in range(n + 1)] # 状态初始化 dp[0][0], dp[0][1], ... = x, y, ... # DP状态的推导 for i in range(m): for j in range(n): dp[i][j] = min(dp[i-1][j], dp[i][j-1]) return dp[m][n] # 最优解 二、例题1、爬楼梯此题为leetcode第70题。 假设我们现在在第n个阶梯,我们要求的是走到第n个阶梯有多少种走法,即$F(n)$。我们知道只能走1步或2步,那么要走到第n个阶梯,只能从第n-1个阶梯或第n-2个阶梯走,那么走到第n个阶梯的走法个数等于走到第n-1个阶梯的走法个数加上走到第n-2个阶梯的走法个数。上述过程我们可以总结出此题的状态及状态转移方程: 状态:$F(n)$,到第n个阶梯的走法个数 状态转移方程:$F(n)=F(n-1)+F(n-2)$ class Solution: def climbStairs(self, n: int) -> int: if n <= 2: return n a, b = 1, 2 for i in range(2, n): c = a + b b, a = c, b return c 时间复杂度:$O(n)$ 空间复杂度:$O(1)$ 2、三角形最小路径和此题为leetcode第120题 三角形是个二维数组,我们要求得从最上面的$(0, 01)$出发,到最底下一层的最小路径和,即DF(0, 0)。比如上图的最小路径和的路径是$2 \\to 3 \\to 5 \\to 1$。此题不可以用贪心算法,反例如上面右图所示。要用动态规划,我们要从底向上地去思考。加入我们处于第$i$行,那么从$(i, j)$出发到底部的最小路径和为:(它的左下方格的最小路径和,它的右下方格的最小路径和)的最小值加上它自己。注意我们需要得到是“最小路径和”,而不是方格本身的最小值。由此我们可以确定本题的状态和状态转移方程: 状态:$DF(i, j)$,第$(i, j)$个方格到最底部的最小路径和 状态转移方程:$DF[i, j] = min(DF[i+1, j], DF[i+1,j+1]) + Triangle[i, j]$,初始值为$DF[m-1, j]=Triangle[-1, j]$ class Solution: def minimumTotal(self, triangle: List[List[int]]) -> int: m, n = len(triangle), len(triangle[-1]) DP = triangle[-1] for i in range(m-1)[::-1]: for j in range(len(triangle[i])): DP[j] = min(DP[j], DP[j+1]) + triangle[i][j] return DP[0] 时间复杂度:$O(m \\times n)$ 空间复杂度:$O(n)$ 3、乘积最大子序列此题是leetcode第152题。我们设有一个数组a=[2, 3, -2, 4],其乘积最大的子序列为2, 3,最大乘积为6。假设我们在第i位,因为这个数可能为正也可能为负,所以我们需要记录最大乘积子序列和最小乘积子序列。我们可以这样定义状态和状态转移方程: 状态:$DP_{max}[i]$和$DP_{min}[i]$。代表的意思是,在第$i$个数时,从$0 \\to i$的最大乘积子序列的乘积值,注意这里包括第$i$个数。 状态转移方程:因为$a[i]$可能为正也可能为负,为负时要乘前面的最小子序列的乘积才能变为最大值,因此要区分$a[i]$为正负的情况: DP_{max}[i]= \\begin{cases} DP_{max}[i-1] \\times a[i], a[i] \\geq 0 \\\\ DP_{min}[i-1] \\times a[i], a[i] < 0 \\end{cases} DP_{min}[i]= \\begin{cases} DP_{min}[i-1] \\times a[i], a[i] \\geq 0 \\\\ DP_{max}[i-1] \\times a[i], a[i] < 0 \\end{cases} 最后的结果为$max\\{DP_{max}\\}$ class Solution: def maxProduct(self, nums: List[int]) -> int: if nums is None: return None # 这里我们不需要为nums里的每个元素都开辟一个存储最大最小值的空间 # 只需要当前元素和前一个元素的就行 DP_max, DP_min, res = [nums[0], nums[0]], [nums[0], nums[0]], nums[0] for i in range(1, len(nums)): num = nums[i] x, y = i % 2, (i - 1) % 2 DP_max[x] = max(DP_max[y] * num, DP_min[y] * num, num) DP_min[x] = min(DP_max[y] * num, DP_min[y] * num, num) res = max(DP_max[x], res) return res 时间复杂度:$O(N^2)$ 空间复杂度:$O(1)$ 4、最长上升子序列此题为leetcode第300题。假设有一序列a=[10, 9, 2, 5, 3, 7, 101, 18, 20],其最长上升子序列为2, 3, 7, 18, 20,长度为5。假如我们处于第$i$个位置,对于它前面的每个元素$j$,都有个从0到$j$的最长上升子序列长度,如果$a[i]>a[j]$,那么$a[0]$到$a[j]$的最长上升子序列再加上$a[i]$通用可以构成上升序列。所以对应每个$j$,我们找到他们当中加上$a[j]$后最长上升子序列的长度即可。 状态:$DP[i]$,从0到$i$的最长上升子序列的长度(包含第$i$个元素) 状态转移方程:对于$i \\in[0, n-1]$,$DP[i]=max\\{DP[i], DP[j]+1\\}$,其中$j \\in [0, i-1]$且$a[j] < a[i]$ 最后的结果为$max\\{DP[0], DP[1], \\cdots, DP[n-1]\\}$ class Solution: def lengthOfLIS(self, nums: List[int]) -> int: if nums is None or len(nums) == 0: return 0 DP = [1] * len(nums) res = 1 for i in range(1, len(nums)): for j in range(0, i): if nums[j] < nums[i]: DP[i] = max(DP[j] + 1, DP[i]) res = max(DP[i], res) return res 5、零钱兑换此题为leetcode第322题。设有不同面额的硬币coins=[1, 2, 5],和一个总金额amount=11。这个题可以转为类似爬楼梯的问题(上面第1题),每次可以爬1、2、5步,一共有11级台阶,所需的最少步数是多少。 状态:$DP[i]$,到达第$i$阶时最少的步数 状态转移方程:$DP[i]=min\\{DP[i-coins[j]]\\} +1,j \\in [0, n-1] \\space \\space \\text{and} \\space \\space coins[j] \\leq i$ 最终结果为$DP[amount]$ class Solution: def coinChange(self, coins: List[int], amount: int) -> int: # 初始化一个长度为amount + 1的数组 DP = [0] + [amount+1] * (amount) for i in range(1, amount+1): for coin in coins: if coin <= i: DP[i] = min(DP[i], DP[i-coin] + 1) if DP[amount] > amount: # 说明DP[i]没有被更新,没有可以组成amount的组合 return -1 else: return DP[amount] 时间复杂度:$O(amount \\times n)$ 空间复杂度:$O(amount)$ 6、编辑距离此题为leetcode第72题有两个单词word1和word2,假设我们处于word1的第$i$位和word2的第$j$位,分为两种情况。第一种情况,$w[i]==w[j]$,此时不需要任何变化,此时的最少的操作数为word1的第0到i-1个字符变为word2第0至j-1个字符所需的最少操作数。第二种情况,$w[i]!=w[j]$,那么可能执行三种操作(插入,删除,替换)。如果执行插入操作,那么到此步需要懂得最少操作数为word1的第0至i-1个字符变为word2第0至j个字符所需的最少操作数;如果执行删除操作,那么到此步需要懂得最少操作数为word1的第0至i个字符变为word2第0至j-1个字符所需的最少操作数;如果执行替换操作,那么和第一种情况是类似的。那到底选哪种操作呢,答案是选择使得当前状态最小的操作,即上面的三个操作中取$min$。 状态:$DP[i][j]$,word1的前$i$个字符变为word2的前$j$个字符所需的最少步数 状态转移方程: DP[i][j] = \\begin{cases} DP[i-1][j-1], \\space \\space if \\space \\space word[i] == word[j] \\\\ min\\{\\underbrace{DP[i-1,j]}_{insert}, \\underbrace{DP[i, j-1]}_{delete}, \\underbrace{DP[i-1, j-1]}_{replace}\\} + 1, \\space \\space if \\space \\space word[i] != word[j] \\end{cases} 最终结果为$DP[m][n]$ class Solution: def minDistance(self, word1: str, word2: str) -> int: m, n = len(word1), len(word2) # 初始化一个二维数组,为(m+1) x (n+1)大小 DP = [[0 for _ in range(n + 1)] for _ in range(m + 1)] # 初值 # word1的第0至i个字符变为空需要i步操作 # word1由空变为word的第0至j个字符需要j步操作 for i in range(m + 1): DP[i][0] = i for j in range(n + 1): DP[0][j] = j for i in range(1, m + 1): for j in range(1, n + 1): if word1[i - 1] == word2[j - 1]: DP[i][j] = DP[i - 1][j - 1] else: DP[i][j] = min(DP[i - 1][j], DP[i][j - 1], DP[i - 1][j - 1]) + 1 return DP[m][n] 时间复杂度:$O(m \\times n)$ 空间复杂度:$O(m \\times n)$ 7、股票买卖问题这里我们讲解关于股票买卖的6道系列题:121、122、123、188、309、714。以上题目均可以用一个状态转移方程解决,只需稍微修改既可以。我们以一个通用的情况为例,即“每天可以完成$K$笔交易”。注意:(1)一次交易是包括买和卖的过程;(2)当前有股票时只能卖,不能再次买入 我们设数组$a$的长度为$N$,我们可以设状态为到第$i$天时所获得的最大利润,是一个一维数组。但我们会发现,在写状态转移方程时无法判断当前有无股票(无法判断当前应该买还是卖)、无法判断当前是第几次交易(达到最大交易次数则无法再交易),因此上面两个信息也应该出现在状态里。那么我们需要定义一个三维的状态: 状态的定义:$DP[i][k][j]$,到第$i$天时的最大利润。其中:(1)$k \\in [0, K]$,表示$i$之前总共进行了多少次交易;(2)$j=0 \\space or \\space 1$,0表示当前无股票,只能不动或买,1表示当前有股票,只能不动或卖。 状态转移方程:当处于第$i$天第$k$次交易时,可能出现有股票也可能无股票的情况,即$j$可取值为0或1。当$j=0$即没有股票时,可能是前一天也没有股票,当下也不做交易;也可能是前一天有股票,当下把它卖掉了,注意前一天有股票依然是第$k$次交易,因为只有再次卖掉才算一次交易。同理当$j=1$即有股票时,可能是前一天有股票,当下不做交易;也可能是前一天无股票,当下买入了,注意前一天无股票那么就是已经完成了$k-1$次交易。 DP[i, k, 0]=max \\begin{cases} DP[i-1, k, 0] \\text{,前一天无股票,不动}\\\\ DP[i-1, k, 1] + a[i] \\text{,前一天有股票,卖掉} \\end{cases} DP[i, k, 1]=max \\begin{cases} DP[i-1, k, 1] \\text{,前一天有股票,不动}\\\\ DP[i-1, k-1, 0] - a[i] \\text{,前一天无股票,买入} \\end{cases}最终结果为$max\\{DP[n-1, k, 0], k \\in [0, K]\\}$。在这样的状态和状态转移方程下,时间复杂度为$O(N \\times K)$,空间复杂度为$O(N \\times K)$。 用这么一个状态转移方程就可以解决下面6道类似的题目: 188题: 最多可以完成$K$笔交易 class Solution: def maxProfit(self, K: int, prices: List[int]) -> int: # 特殊情况,只有0天或1天无法完成一次完整的交易,利润为0 if len(prices) <= 1: return 0 # 这里需要对K进行一下讨论:若K大于prices长度的一半,那么实际上可以进行不限次数的交易,和122题的情况一样 # 不区分这样的情况也可以,但在leetcode上会超时 if K < len(prices) // 2: # 定义状态,三维数组 DP = [[[0, 0] for _ in range(K + 1)] for _ in range(len(prices))] # 初始化状态,第0天的每次交易有股票的情况下,利润为-prices[0] for k in range(1, K+1): DP[0][k][1] = -prices[0] # 根据状态方程遍历每天和每天的交易次数 res = 0 for i in range(1, len(prices)): for k in range(1, K+1): DP[i][k][0] = max(DP[i-1][k][0], DP[i-1][k][1] + prices[i]) DP[i][k][1] = max(DP[i-1][k][1], DP[i-1][k-1][0] - prices[i]) if res < DP[i][k][0]: res = DP[i][k][0] return res # 和122题一样的情况,解释见下面的122题 else: DP = [0, -prices[0]] for price in prices[1:]: DP[0] = max(DP[0], DP[1] + price) DP[1] = max(DP[1], DP[0] - price) return DP[0] 121题:只能进行一次交易 class Solution: def maxProfit(self, prices: List[int]) -> int: if len(prices) <= 1: return 0 # 只能进行一次交易的话可以把K这一维去掉,同时我们只关注相邻的状态,DP的空间复杂度可以由O(N)变为O(1) DP = [0, -prices[0]] for price in prices[1:]: DP[0] = max(DP[0], DP[1] + price) DP[1] = max(DP[1], -price) return DP[0] 122题: 可以交易无限次 class Solution: def maxProfit(self, prices: List[int]) -> int: if len(prices) <= 1: return 0 DP = [0, -prices[0]] # 可以交易无限次的话K这个维度也没有意义了,可以去掉 for price in prices[1:]: DP[0] = max(DP[0], DP[1] + price) DP[1] = max(DP[1], DP[0] - price) return DP[0] 123题: 最多可以完成2笔交易 class Solution: def maxProfit(self, prices: List[int]) -> int: if len(prices) <= 1: return 0 # 是188题的特殊情况,可以直接设K=2 K = 2 DP = [[[0, 0] for _ in range(K + 1)] for _ in range(len(prices))] for k in range(1, K+1): DP[0][k][1] = -prices[0] res = 0 for i in range(1, len(prices)): for k in range(1, K+1): DP[i][k][0] = max(DP[i-1][k][0], DP[i-1][k][1] + prices[i]) DP[i][k][1] = max(DP[i-1][k][1], DP[i-1][k-1][0] - prices[i]) if res < DP[i][k][0]: res = DP[i][k][0] return res 309题: 不限交易次数,但有一天冷冻期,即卖出股票后,你无法在第二天买入股票 class Solution: def maxProfit(self, prices: List[int]) -> int: if len(prices) <= 1: return 0 DP = [0, -prices[0]] prev_prev = 0 # 只有在前前一天卖掉后才能买 for price in prices[1:]: temp = DP[0] # 用一个临时变量保存前一天的DP[0] DP[0] = max(DP[0], DP[1] + price) DP[1] = max(DP[1], prev_prev - price) prev_prev = temp # 当前操作完成后,temp就成了前前一天 return DP[0] 714题: 不限交易次数,但买的时候有交易费。 class Solution: def maxProfit(self, prices: List[int], fee: int) -> int: if len(prices) <= 1: return 0 # 整体和122题一样 DP = [0, -prices[0]-fee] # 在买的时候减去交易费 for price in prices[1:]: DP[0] = max(DP[0], DP[1] + price) DP[1] = max(DP[1], DP[0] - price - fee) # 买的时候减去交易费 return DP[0] 总结动态规划的题目最重要的是定义好状态和状态转移方程,同时要注意边界条件 参考文献[1] https://www.cnblogs.com/hithongming/p/9229871.html[2] https://blog.csdn.net/ailaojie/article/details/83014821","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"算法","slug":"算法","permalink":"http://yoursite.com/tags/算法/"},{"name":"数据结构","slug":"数据结构","permalink":"http://yoursite.com/tags/数据结构/"}]},{"title":"数据结构与算法———位运算","slug":"数据结构与算法———位运算","date":"2020-06-04T07:10:49.000Z","updated":"2020-06-04T07:11:30.000Z","comments":true,"path":"2020/06/04/数据结构与算法———位运算/","link":"","permalink":"http://yoursite.com/2020/06/04/数据结构与算法———位运算/","excerpt":"","text":"一、位运算位运算: 直接对整数在内存中的二进制位进行操作。比如6的二进制是110,11的二进制是1011,那么6 and 11的结果就是2,它是二进制对应位进行逻辑运算的结果。由于位运算直接在内存数据进行操作,不需要转为十进制,因此速度非常快。 常用的逻辑运算符:| 符号 | 描述 | 运算规则 || —— | —— | —————————————————————————————— || & | 与 | 两个都为1时,结果才为1 || | | 或 | 两个都为 0时,结果才为1 || ^ | 异或 | 相同为0,不同为1 || ~ | 取反 | 0变1,1变0 || << | 左移 | 各二进制位全部左移若干位,高位丢弃,低位补0 || >> | 右移 | 各二进制位全部右移若干位,对于无符号数,高位补0,;对于有符号数,有的补符号位(算数右移),有的补0(逻辑右移) | 异或的特点: x^0 = x x^1s = ~x。1s的意思是全为1的二进制数 x^(~x) = 1s x^x = 0 a^b = c $\\rightarrow$ a^c = b, b^c = a。用来交换a、b的值。比如,设a = 1001, b = 1100,那么c = a^b = 0101,则a^c = 1100, b^c = 1001,正好可以交换a、b的值。 a^b^c = a^(b^c) = (a^b)^c 常用的位运算操作: 判断奇偶数:x & 1 ==1为奇数,x & 1 == 0为偶数 清零最低位的1:x & (x-1) 得到最低位的1:x & -x。-x为取反再加1 更为复杂的位运算操作: 将x最右边的n位清零:x & (~ 0<<n) 获取x的第n位值(0或1):(x >> n) & 1 获取x的第n位幂值:x & (1 << (n-1)) 仅将第n位置为1:x | (1<<n) 仅将第n位置为0:x & (~(1<<n)) 将x最高位至第n位(含)清零:x & ((1 << n)-1) 将x第n位至第0位(含)清零:x & (~((1 << (n+1))-1)) 二、例题(1)位1的个数:此题为leetcode第191题。用“常用的位运算操作”里的“清零最低位的1”class Solution: def hammingWeight(self, n: int) -> int: res = 0 while n != 0: res += 1 n = n & (n - 1) return res (2)2的幂:此题为leetcode第231题。2的幂化成2进制,它们都只有一个1,只要判断1的个数即可class Solution: def isPowerOfTwo(self, n: int) -> bool: return n != 0 and n & (n - 1) == 0 (3)比特位计数此题为leetcode第338题给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。 class Solution: def countBits(self, num: int) -> List[int]: res = [0] * (num + 1) for n in range(1, num + 1): res[n] += (res[n&(n-1)] + 1) return res (4)N皇后II,位运算解法此题为leetcode第52题,常规用深度优先搜索解法,会用到集合去保存横竖、斜角方向上的状态,解法点这里。而使用位运算可以直接获得棋盘上可以放皇后的位置,可以提高效率。class Solution: # 使用位运算解决 def totalNQueens(self, n): if n < 1: return [] self.count = 0 self.DFS(n, 0, 0, 0, 0) return self.count def DFS(self, n, row, col, pie, na): if row >= n: self.count += 1 return # 获得可以放皇后的位置 bits = (~(col | pie | na)) & ((1 << n) - 1) # 递归 while bits: p = bits & -bits # 获得最低位的1,也就是遍历合法的位置 self.DFS(n, row + 1, (col | p), (pie | p) << 1, (na | p) >> 1) bits = bits & (bits - 1) # 去掉最低位的1","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"算法","slug":"算法","permalink":"http://yoursite.com/tags/算法/"},{"name":"数据结构","slug":"数据结构","permalink":"http://yoursite.com/tags/数据结构/"}]},{"title":"数据结构与算法———哈希","slug":"数据结构与算法———哈希","date":"2020-06-04T07:09:34.000Z","updated":"2020-06-04T07:10:32.000Z","comments":true,"path":"2020/06/04/数据结构与算法———哈希/","link":"","permalink":"http://yoursite.com/2020/06/04/数据结构与算法———哈希/","excerpt":"","text":"一、哈希表1、哈希表的原理哈希表是一种使用哈希函数组织数据,以支持快速插入和搜索的数据结构。哈希表由直接寻址表和哈希函数构成。哈希函数$h(K)$将元素关键字$K$作为自变量,返回元素的存储下标。直接寻址表如下所示:$U$为键值的全域,包含了所有可能的键值,$K$为实际使用的键值,$T$为直接寻址表,每个关键字对应表中的一个位置,比如2、3、5、8这几个关键字对应了$T(2),T(3),T(5),T(8)$。$T(K)$则指向了关键字为$K$的元素。但直接寻址有这样的缺点:(1)当$U$很大时,需要消耗大量的内存;(2)若$U$很大而$K$很小,则有很多空间被浪费;(3)无法处理关键字不是数字的情况。为改进这些缺点,可以使用哈希函数。直接寻址表是键值为$K$的元素放到$T(K)$位置上,而哈希函数构建一个大小为$m$的寻址表$T$,键值为$K$的元素放到$h(K)$的位置上。$h(K)$是一个函数,将域$U$映射到表$T[0, 1, …, m-1]$。假设有一个长度为7的哈希表,哈希函数为$h(K)=K\\%7$。元素集合$\\{14,22,3,5\\}$的存储方式如下图:14余7等于0,存在了$T(0)$的位置,22余7等于1,存在了$T(1)$的位置。这样做也会有问题,比如再存一个键值为15的元素,15余7等于1,但此时$T(1)$已经被占了,这就是哈希冲突。如果位置$i$被占,可以解决哈希冲突的方法: 线性探查:探查$i+1,i+2,…$这些位置 二次探查:$i+1^2, i-1^2, i+2^2, i-2^2,…$这些位置 二度哈希:有$n$个哈希函数,当第一个哈希函数冲突时,则使用第二个、第三个…… 拉链法:哈希表每个位置都连一个链表,冲突的元素将被加到该位置链表的最后。如下图所示:常用的哈希函数: 除法哈希:$h(K)=K\\%m$ 乘法哈希:$h(K)=floor(m \\times (A*K\\%1))$ 全域哈希:$h(K)=((a \\times K + b) \\% p) \\% m \\space \\space \\space \\space a,b=1,2,…p-1$ 2、python里哈希的用法哈希表由两种不同的类型:哈希集合和哈希映射 哈希集合是集合数据结构的实现之一,用于存储非重复值。python里可以用集合set实现: # 1. 初始化哈希集合 hashset = set() # 2. 添加新的键 hashset.add(3) hashset.add(2) hashset.add(1) # 3. 删除键 hashset.remove(2) # 4. 检查键是否在哈希集合里 if (2 not in hashset): print(\"Key 2 is not in the hash set.\") # 5. 获得哈希集合的大小 print(\"Size of hashset is:\", len(hashset)) # 6. 迭代哈希集合 for x in hashset: print(x, end=\" \") print(\"are in the hash set.\") # 7. 清空哈希集合 hashset.clear() print(\"Size of hashset:\", len(hashset)) 哈希映射是映射 数据结构的实现之一,用于存储(key, value)键值对。在python可以用字典dict实现: # 1. 初始化哈希映射 hashmap = {0 : 0, 2 : 3} # 2. 插入(key, value) 或者更新已存在的键值对 hashmap[1] = 1 hashmap[1] = 2 # 3. 获得键值 print(\"The value of key 1 is: \" + str(hashmap[1])) # 4. 删除键 del hashmap[2] # 5. 检查键是否在哈希集合里 if 2 not in hashmap: print(\"Key 2 is not in the hash map.\") # 6. 键和键值可以有不同的数据类型 hashmap[\"pi\"] = 3.1415 # 7. 获得哈希映射的大小 print(\"The size of hash map is: \" + str(len(hashmap))) # 8. 迭代哈希映射 for key in hashmap: print(\"(\" + str(key) + \",\" + str(hashmap[key]) + \")\", end=\" \") print(\"are in the hash map.\") # 9. 获得所有键 print(hashmap.keys()) # 10. 清空哈希映射 hashmap.clear(); print(\"The size of hash map is: \" + str(len(hashmap))) 二、例题1、哈希集合的应用存在重复元素: 此题为leetcode第217题。def containsDuplicate(self, nums: List[int]) -> bool: hash_ = set() for num in nums: if num not in hash_: hash_.add(num) else: return True return False 时间复杂度 : $O(n)$。 空间复杂度 : $O(n)$。哈希表占用的空间与元素数量是线性关系。 两个数组的集合: 此题为leetcode第349题class Solution: def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: set1 = set(nums1) set2 = set(nums2) return list(set2 & set1) 时间复杂度:$O(m+n)$,其中 $n$ 和 $m$ 是数组的长度。$O(n)$ 的时间用于转换 nums1 在集合中,$O(m)$ 的时间用于转换 nums2 到集合中,并且平均情况下,集合的操作为 $O(1)$。空间复杂度:$O(m+n)$,最坏的情况是数组中的所有元素都不同。 2、哈希映射的应用(1)场景一:用映射来提供很多的信息哈希集合没有映射,只是单一地存储了不同的键。而哈希映射可以将键映射到键值,提供更多的信息。两数之和: 此题为leetcode第1题 class Solution: def twoSum(self, nums: List[int], target: int) -> List[int]: # 哈希 hash_ = {} for i, a in enumerate(nums): b = target - a if b in hash_: return [i, hash_[b]] hash_[a] = i return None 时间复杂度:$O(n)$,我们只遍历了包含有 $n$ 个元素的列表一次。在表中进行的每次查找只花费 $O(1)$ 的时间。 空间复杂度:$O(n)$,所需的额外空间取决于哈希表中存储的元素数量,该表最多需要存储 $n$ 个元素。 三数之和: 此题为leetcode第15题 class Solution: def threeSum(self, nums: List[int]) -> List[List[int]]: if len(nums) < 3: return [] # 先对数组排序, 遍历数组遇到与前一个元素相同的情况可直接跳过 nums.sort() res = set() for i, x in enumerate(nums[:-2]): if i >= 1 and nums[i-1] == x: continue hash_ = {} for j, y in enumerate(nums[i+1:]): if y not in hash_: hash_[-y-x] = 1 else: res.add((x, -x - y, y)) return list(res) 时间复杂度:$O(n^2)$ 空间复杂度:$O(n)$ 同构字符串: 此题为leetcode第205题 class Solution: def isIsomorphic(self, s: str, t: str) -> bool: hash_ = {} for i, a in enumerate(s): if a in hash_: if hash_[a] != t[i]: return False elif t[i] in hash_.values(): return False else: hash_[a] = t[i] return True 两个列表的最小索引和: 此题为leetcode第599题 class Solution: def findRestaurant(self, list1: List[str], list2: List[str]) -> List[str]: hash_ = {} temp = len(list1) + len(list2) res = [] for i, a in enumerate(list1): hash_[a] = i for j, b in enumerate(list2): if b in hash_: if hash_[b] + j == temp: res.append(b) elif hash_[b] + j < temp: res = [] res.append(b) temp = hash_[b] + j return res (2)场景二:按键聚合这类问题是统计键出现的次数,所以对应的键值为该键出现的次数两个数组的交际II: 此题为leetcode第350题 class Solution: def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]: hash_ = {} res = [] for num in nums1: if num not in hash_: hash_[num] = 1 else: hash_[num] += 1 for num in nums2: if num in hash_ and hash_[num] != 0: res.append(num) hash_[num] -= 1 return res 时间复杂度:$O(n+m)$。其中 $n,m$ 分别代表了数组的大小。空间复杂度:$O(n)$。如果对较小的数组进行哈希映射使用的空间则为$O(min(n,m))$。 存在重复元素II: 此题为leetcode第219题。解法动画class Solution: def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool: hash_ = set() for i, num in enumerate(nums): if num not in hash_: hash_.add(num) else: return True if len(hash_) > k: hash_.remove(nums[i-k]) return False 时间复杂度:$O(n)$。我们会做 $n$ 次搜索、删除、插入 操作,每次操作都耗费常数时间。 空间复杂度:$O(min(n,k))$。开辟的额外空间取决于散列表中存储的元素的个数,也就是滑动窗口的大小 $O(min(n,k))$。 (3)设计键上面的题中,键都是显而易见的,但有时候需要设计合适的键。字母异位词分组: 此题为leetcode第49题class Solution: def groupAnagrams(self, strs: List[str]) -> List[List[str]]: hash_ = collections.defaultdict(list) for str in strs: hash_[tuple(sorted(str))].append(str) return list(hash_.values()) 时间复杂度:$O(NKlogK)$,其中 $N$ 是 strs的长度,而 $K$ 是 strs 中字符串的最大长度。当我们遍历每个字符串时,外部循环具有的复杂度为 $O(N)$。然后,我们在 $O(KlogK)$ 的时间内对每个字符串排序。 空间复杂度:$O(NK)$,排序存储在 ans 中的全部信息内容。 寻找重复子树: 此题为leetcode第652题 class Solution: def findDuplicateSubtrees(self, root: TreeNode) -> List[TreeNode]: count = collections.Counter() ans = [] def collect(node): if not node: return \"#\" serial = \"{},{},{}\".format(node.val, collect(node.left), collect(node.right)) count[serial] += 1 if count[serial] == 2: ans.append(node) return serial collect(root) return ans 时间复杂度:$O(N^2)$,其中 $N$ 是二叉树上节点的数量。遍历所有节点,在每个节点处序列化需要时间 $O(N)$。 空间复杂度:$O(N^2)$,count 的大小。 设计键总结(参考自leetcode): 当字符串 / 数组中每个元素的顺序不重要时,可以使用排序后的字符串 / 数组作为键: 如果只关心每个值的偏移量,通常是第一个值的偏移量,则可以使用偏移量作为键: 在树中,可能会直接使用 TreeNode 作为键。 但在大多数情况下,采用子树的序列化表述可能是一个更好的主意。 在矩阵中,可以使用行索引或列索引作为键。 在数独中,可以将行索引和列索引组合来标识此元素属于哪个块 有时,在矩阵中,您可能希望将值聚合在同一对角线中","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"算法","slug":"算法","permalink":"http://yoursite.com/tags/算法/"},{"name":"数据结构","slug":"数据结构","permalink":"http://yoursite.com/tags/数据结构/"}]},{"title":"数据结构与算法———递归","slug":"数据结构与算法———递归","date":"2020-06-04T07:06:55.000Z","updated":"2020-06-04T07:07:38.000Z","comments":true,"path":"2020/06/04/数据结构与算法———递归/","link":"","permalink":"http://yoursite.com/2020/06/04/数据结构与算法———递归/","excerpt":"","text":"一、递归递归:在定义一个过程或函数时,出现本过程或本函数的成分称为递归。递归条件:可以用递归解决的问题应该满足以下三个条件: 这个问题可以转化为一个或多个子问题来求解,而且这些子问题的求解方法与原问题完全相同 递归调用的次数是有限的 必须有终止条件 递归的底层原理: 函数调用操作包括从一块代码到另一块代码之间的双向数据传递和执行控制转移,大多数CPU使用栈来支持函数调用。单个函数调用操作所使用的的函数调用栈被称为栈帧。每次函数调用都会相应地创建一帧,返回函数地址、函数实参和局部变量值等,并将该帧压入调用栈。若该函数在返回之前又发生了新的调用,则将新函数对于的帧压入栈,成为栈顶。函数一旦执行完,对应的帧边出栈,控制权交给该函数的上层调用函数,并按照该帧保存的返回地址,确定程序中继续执行的位置。 递归问题一般解决步骤: 对问题$f(s_{n})$进行分析,假设出合理的小问题$f(s_{n-1})$ 假设小问题$f(s_{n-1})$是可解的,在此基础上确定大问题$f(s_{n})$的解,即给出$f(s_{n})$与$f(s_{n-1})$之间的关系 确定一个特殊情况(如$f(s_{1})$或$f(s_{0})$的解),由此作为递归出口 时间复杂度: 时间复杂度$O(T)$通常是递归调用的数量(记作 $R$) 和计算的时间复杂度的乘积(表示为 $O(s)$)的乘积: O(T)=R*O(s)空间复杂度: 在计算递归算法的空间复杂度时,应该考虑造成空间消耗的两个部分:递归相关空间(recursion related space)和非递归相关空间(non-recursion related space) 代码模板: def recursion(level, param1, param2, ...): # 递归终止条件 if level > MAX_LEVEL: return result # 当前level的逻辑 process_data(level, data) # 递归 self.recurison(level, param1, param2, ...) # 如果必要的话,还原此层的状态 reverse_state(level) 二、例题1、两两交换链表中的节点此题为leetcode第24题。示例:给定 1->2->3->4, 你应该返回 2->1->4->3。我们假设有一个链表,已经走到了中间某个节点,当前节点为head,这个一般情况可以表示如下:我们希望head和next能够互换,希望达到如下效果: 我们希望head和next互换,互换后head指向后面,next变为当前子链表的头结点 交换后,head应该指向后面的子链表,而后面的子链表应该是交换完成了的,也就是说,后面的子链表两两交换是个子问题,解决方式和当前的方式一样,所以这个子链表的头结点(即head.next.nex应该传入递归函数中) 因为交换后next为当前子链表的头结点,我们用一个指针指向它,即res=head.next,交换完后我们返回res即可,因为当前子链表也是上一层递归调用的子问题,我们要返回当前链表的头结点 交换的过程:首先我们需要将head.next指向后面子问题返回的头结点,然后next指向head,即res.next=head 交换完后,返回此时当前子链表的头结点res即可 终止条件:当head=None(无节点)或head.next=None(此时为最后一个节点)时,直接返回head class Solution: def swapPairs(self, head: ListNode) -> ListNode: # 终止条件 if head == None or head.next == None: return head # 交换后的头结点 res = head.next # 子问题递归,head指向子问题返回来的节点 head.next = self.swapPairs(head.next.next) # next指向head res.next = head return res 时间复杂度:$O(N)$,其中 $N$ 指的是链表的节点数量 空间复杂度:$O(N)$,递归过程中使用的堆栈空间 2、反转链表此题为leetcode第206题。示例:输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL假设我们有如下链表:当前节点为head,假设子问题返回的链表已经两两反转,我们希望节点2能指向节点1,节点1指向None。递归过程如下图所示: 当前节点为head,我们期望后续的子链表指向head,而head指向None。那么后续子链表的两两交换问题为子问题。 子问题里的子链表节点两两交换后,原来的尾节点(图中的节点4)变成了头结点,并且要返回这个头结点;原来的头结点(图中的节点2)变为了尾节点,并且要指向None,因为尾节点最后都要指向None。注意此时head节点依然是指向节点2的,因为子问题并没有改变head的指向 交换过程:需要将节点2指向节点1,即head.next.next=head,然后head指向None 终止条件:head=None(无节点)或head.next=None(最后一个节点)时,直接返回head class Solution: def reverseList(self, head: ListNode) -> ListNode: # 终止条件 if not head or not head.next: return head # 子问题递归,返回交换完后的子链表的头结点 res = self.reverseList(head.next) # 交换 head.next.next = head head.next = None return res 时间复杂度:$O(N)$,其中 $N$ 指的是链表的节点数量 空间复杂度:$O(N)$,递归过程中使用的堆栈空间3、斐波那契数列记忆化此题为leetcode第509题。斐波那契数列有如下递归关系: f(0)=0, f(1)=1 \\\\ f(n)=f(n-1) + f(n-2),n>1给定$n$计算$f(n)$。以计算$f(4)$为例,我们可以得到:$f(4)=f(3)+f(2)=f(2)+f(1)+f(1)+f(0)=f(1)+f(0)+f(1)+f(1)+f(0)$,把这个递归过程画出来如下图所示:按说这个很容易找到子问题,并且终止条件也明确,但是我们观察发现,这其中有很多重复的计算,比如$f(2)$重复了两次,这会导致内存占用较多。为此,我们可以将中间结果暂存起来,到时候直接用就可以。 class Solution: def fib(self, N: int) -> int: # 缓存字典 catch = {} def rec(N): # 若N在缓存中,直接返回对应值 if N in catch: return catch[N] # N小于2时直接返回自己 if N < 2: res = N else: res = rec(N-1) + rec(N-2) # 放入缓存中 catch[N] = res return res return rec(N) 时间复杂度:$O(N)$ 空间复杂度:$O(N)$,缓存空间大小 注意这里我们使用了一个额外的函数rec(N),然后直接在fib函数中直接返回rec(N),我们称这样的递归为尾递归:尾递归函数是递归函数的一种,其中递归调用是递归函数中的最后一条指令。并且在函数中应该只有一次递归调用。 尾递归的好处是,它可以避免递归调用期间栈空间开销的累积,因为系统可以为每个递归调用重用栈中的固定空间。 4、合并两个有序链表此题为leetcode第21题。示例:输入:1->2->4, 1->3->4 输出:1->1->2->3->4->4。我们假设有如下两个有序链表:设l1指向了第一个链表中的某个位置,l2指向了第二个链表中的某个位置,我们比较这两个节点的大小。如果l1<l2,那么l1应该在l2之后,l2应该和l1之后的子链表继续比较,由此形成子问题。如果l1>l2,那么l2应该在l1之后,l1继续和l2之后的子链表比较。整个过程如下所示: 若l1<l2,则将l1.next和l2传入递归函数中,将l1指向递归函数返回的节点;反之将l2.next和l1传入递归函数中,l2指向递归函数返回的节点。 当l1=None时,说明l1已提前遍历完(或l1本来为空),l2剩下的也不用比较了,直接返回l2;同理l2=None时,直接返回l1。 class Solution: def mergeTwoLists(self, l1, l2): if l1 is None: return l2 elif l2 is None: return l1 elif l1.val < l2.val: l1.next = self.mergeTwoLists(l1.next, l2) return l1 else: l2.next = self.mergeTwoLists(l1, l2.next) return l2 时间复杂度:$O(n + m)$。因为每次递归调用都会将指向 l1 或 l2 的指针递增一次(逐渐接近每个列表末尾的 null),所以每个列表中的每个元素都会对 mergeTwoLists 进行一次调用。 因此,时间复杂度与两个列表的大小之和是线性相关的。 空间复杂度:$O(n + m)$。一旦调用 mergetwolist,直到到达 l1或 l2 的末尾时才会返回,因此 $n + m$的栈将会消耗 $O(n + m)$ 的空间。 5、Pow(x, n)此题为leetcode第50题 class Solution: def myPow(self, x: float, n: int) -> float: # 递归1 # def func(x, n): # if n == 0: # return 1 # if x == 0: # return 0 # temp = func(x, n >> 1) # # 位运算判断奇偶 # if n & 1: # n为奇数 # return temp * temp * x # else: # n为偶数 # return temp * temp # if n >= 0: # res = func(x, n) # else: # res = 1 / func(x, -n) # return res # 递归2 # if n == 0: # return 1 # if n < 0: # return 1. / self.myPow(x, -n) # if n % 2: # 如果n是奇数,在下一层算n/2 # return x * self.myPow(x, n - 1) # else: # 如果n是偶数 # return self.myPow(x * x, n/2) # 非递归 if n < 0: x = 1. / x n = -n res = 1 while n: if n & 1: # n为奇数 res *= x x *= x n >>= 1 # 右移一位,相当于n // 2 return res","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"算法","slug":"算法","permalink":"http://yoursite.com/tags/算法/"},{"name":"数据结构","slug":"数据结构","permalink":"http://yoursite.com/tags/数据结构/"}]},{"title":"数据结构与算法———广度与深度优先搜索","slug":"数据结构与算法———广度与深度优先搜索","date":"2020-06-04T07:05:46.000Z","updated":"2020-06-04T07:06:36.000Z","comments":true,"path":"2020/06/04/数据结构与算法———广度与深度优先搜索/","link":"","permalink":"http://yoursite.com/2020/06/04/数据结构与算法———广度与深度优先搜索/","excerpt":"","text":"一、广度优先搜索广度优先搜索(BFS,Breadth First Search) 的一个常见应用是找出从根结点到目标结点的最短路径,其实现用到了队列。下面用一个例子来说明BFS的原理,在下图中,我们BFS 来找出根结点 A 和目标结点 G 之间的最短路径。 图3:BFS例子 首先初始化一个队列Q,将根节点入队:A A出队,将与A相邻的节点入队,此时队列为BCD B出队,将与B相邻的节点入队,此时队列为CDE C出队,将与C相邻的节点入队,此时队列为DEF(节点E已经被遍历过,不需要再入队) D出队,将与D相邻的节点入队,此时队列为EFG E出队,下面没有节点 F出队,下面是G,已遍历过,为目标值,遍历结束 BFS代码模板: def BFS(graph, start, end): queue = [] queue.append([start]) visited.add(start) # 对于图来说,要标记节点已经被访问过,防止重复访问 while queue: node = queue.pop() # 出队 visited.add(node) # 标记访问 process(node) # 对节点进行一些处理 nodes = generate_related_nodes(node) # 得到当前节点的后继节点,并且没有被访问过 queue.push(nodes) # 其他处理部分 ... 二、深度优先搜索与 BFS 类似,深度优先搜索(DFS,Depth-First-Search) 也可用于查找从根结点到目标结点的路径。下面通过一个例子,用DFS 找出从根结点 A 到目标结点 G 的路径。 图3:BFS例子 在上面的例子中,我们从根结点 A 开始。首先,我们选择结点 B的路径,并进行回溯,直到我们到达结点 E,我们无法更进一步深入。然后我们回溯到A并选择第二条路径到结点 C。从 C 开始,我们尝试第一条路径到 E 但是 E已被访问过。所以我们回到 C 并尝试从另一条路径到 F。最后,我们找到了 G。虽然我们成功找出了路径 A-> C-> F-> G ,但这不是从 A 到 G 的最短路径。 DFS代码模板: # 递归写法 visited = set() def DFS(node, visited): visited.add(node) # process current node here ... for next_node in node.children(): if next_node not in visited: DFS(next_node, visited) 三、例题(1)岛屿数量问题此题为leetcode的第200题。有两个思路: Flood fill 算法: 深度优先搜索或广度优先搜索 并查集 Flood fill 算法的含义:是从一个区域中提取若干个连通的点与其他相邻区域区分开(或分别染成不同颜色)的经典 算法。因为其思路类似洪水从一个区域扩散到所有能到达的区域而得名。在 GNU Go 和 扫雷 中,Flood Fill算法被用来计算需要被清除的区域。 对于这类问题,其实就是从一个点开始,遍历与这个起点连着的格子,遍历过的格子就标上“被访问过”的记号,找到与之相连的所有格子后,就是发现了一个岛屿。而这个遍历的过程可以用广度优先或深度优先实现,具体讲解可以参考这里,动画很明白,这里只贴上代码: # 广度优先搜索 from typing import List from collections import deque class Solution: # x-1,y # x,y-1 x, y x,y+1 # x+1,y # 方向数组,它表示了相对于当前位置的 4 个方向的横、纵坐标的偏移量,这是一个常见的技巧 directions = [(-1, 0), (0, -1), (1, 0), (0, 1)] def numIslands(self, grid: List[List[str]]) -> int: m = len(grid) if m == 0: return 0 n = len(grid[0]) marked = [[False for _ in range(n)] for _ in range(m)] count = 0 # 从第 1 行、第 1 格开始,对每一格尝试进行一次 DFS 操作 for i in range(m): for j in range(n): # 只要是陆地,且没有被访问过的,就可以使用 BFS 发现与之相连的陆地,并进行标记 if not marked[i][j] and grid[i][j] == '1': # count 可以理解为连通分量,你可以在广度优先遍历完成以后,再计数, # 即这行代码放在【位置 1】也是可以的 count += 1 queue = deque() queue.append((i, j)) # 注意:这里要标记上已经访问过 marked[i][j] = True while queue: cur_x, cur_y = queue.popleft() # 得到 4 个方向的坐标 for direction in self.directions: new_i = cur_x + direction[0] new_j = cur_y + direction[1] # 如果不越界、没有被访问过、并且还要是陆地,就入队 if 0 <= new_i < m and 0 <= new_j < n and not marked[new_i][new_j] and grid[new_i][new_j] == '1': queue.append((new_i, new_j)) # 入队后马上要标记为“被访问过” marked[new_i][new_j] = True #【位置 1】 return count # 深度优先搜索 from typing import List class Solution: # x-1,y # x,y-1 x,y x,y+1 # x+1,y # 方向数组,它表示了相对于当前位置的 4 个方向的横、纵坐标的偏移量,这是一个常见的技巧 directions = [(-1, 0), (0, -1), (1, 0), (0, 1)] def numIslands(self, grid: List[List[str]]) -> int: m = len(grid) if m == 0: return 0 n = len(grid[0]) marked = [[False for _ in range(n)] for _ in range(m)] count = 0 # 从第 1 行、第 1 格开始,对每一格尝试进行一次 DFS 操作 for i in range(m): for j in range(n): # 只要是陆地,且没有被访问过的,就可以使用 DFS 发现与之相连的陆地,并进行标记 if not marked[i][j] and grid[i][j] == '1': # 连通分量计数 count += 1 # DFS搜索 self.__dfs(grid, i, j, m, n, marked) return count # 递归进行深度优先搜索 def __dfs(self, grid, i, j, m, n, marked): marked[i][j] = True for direction in self.directions: new_i = i + direction[0] new_j = j + direction[1] if 0 <= new_i < m and 0 <= new_j < n and not marked[new_i][new_j] and grid[new_i][new_j] == '1': self.__dfs(grid, new_i, new_j, m, n, marked) (2)二叉树的最大深度此题为leetcode第104题给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。深度优先解法:# 深度优先搜索 class Solution: def maxDepth(self, root: TreeNode) -> int: # 递归,深度优先搜索 if root is None: return 0 else: left_height = self.maxDepth(root.left) # 左子树高度 right_height = self.maxDepth(root.right) # 右子树高度 return max(left_height, right_height) + 1 广度优先解法:# 迭代,广度优先搜索 from collections import deque class Solution: def maxDepth(self, root: TreeNode) -> int: if root is None: return 0 depth = 0 q = deque() q.append((1, root)) while q: curr_depth, node = q.pop() depth = max(depth, curr_depth) if node.left: q.append((curr_depth + 1, node.left)) if node.right: q.append((curr_depth + 1, node.right)) return depth (3)二叉树的最小深度此题为leetcode第111题给定一个二叉树,找出其最小深度。最小深度是从根节点到最近叶子节点的最短路径上的节点数量。深度优先解法:# 递归,深度优先搜索 class Solution: def minDepth(self, root: TreeNode) -> int: # 递归,深度优先搜索 if root is None: return 0 if root.left is None: # 若左子树为空,root的深度为1+右子树深度 return 1 + self.minDepth(root.right) if root.right is None: # 若右子树为空,root的深入为1+左子树深度 return 1 + self.minDepth(root.left) left_height = self.minDepth(root.left) # 左子树高度 right_height = self.minDepth(root.right) # 右子树高度 return min(left_height, right_height) + 1 广度优先解法:# 迭代,广度优先搜索 from collections import deque class Solution: def minDepth(self, root: TreeNode) -> int: # 迭代,广度优先搜索 if root is None: return 0 q = deque() q.append((1, root)) depth = 1 while q: curr_depth, node = q.popleft() # 如果node为叶子节点,则此时深度为最小深度 if node.left is None and node.right is None: return curr_depth if node.left: q.append((curr_depth + 1, node.left)) if node.right: q.append((curr_depth + 1, node.right)) (4)括号生成此题为leetcode第22题数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。 # 深度优先搜索加剪枝 class Solution: def generateParenthesis(self, n: int) -> List[str]: self.res = [] self._gen('', 0, 0, n) return self.res def _gen(self, result, left, right, n): # left为左括号用了几个,right为右括号用了几个 if left == n and right == n: self.res.append(result) return # 如果用的左括号小于n个,可以加左括号 if left < n: self._gen(result+'(', left+1, right, n) # 只有在用的右括号小于用的左括号时,才可以加右括号 if right < left: self._gen(result+')', left, right+1, n) (5)N皇后问题此题为leetcode第51题n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。 class Solution: def solveNQueens(self, n: int) -> List[List[str]]: if n < 1: return [] self.res = [] # 记录最终结果 self.cols, self.pie, self.na = set(), set(), set() # 标记横竖、斜角方向 self.DFS(n, 0, []) return [['.' * j + 'Q' + '.' * (n - j - 1) for j in col] for col in self.res] def DFS(self, n, row, curr_state): # curr_state代表每行的哪些列可以放置皇后,是一种可能的解决方案 # 如果递归可以走到最后一层,说明curr_state是一种合法的解决方案,将其放入self.res里 if row >= n: self.res.append(curr_state) return # 遍历列 for col in range(n): # 如果当前位置上,可以被横竖、斜角方向上的皇后攻击到,则continue if col in self.cols or row + col in self.pie or row - col in self.na: continue # 如果当前位置不会被攻击到,则标记横竖、斜角方向 self.cols.add(col) self.pie.add(row + col) self.na.add(row - col) # 递归,row+1进入下一行,curr_state+[col]保存一种可能的状态 self.DFS(n, row + 1, curr_state + [col]) # 上面的递归出来后,清除横竖、斜角方向的标记 # 因为我们是在(n, col)位置上尝试放置皇后看是否可行 self.cols.remove(col) self.pie.remove(row + col) self.na.remove(row - col) (6)解数独此题为leetcode第37题编写一个程序,通过已填充的空格来解决数独问题。下面给出最朴素的DFS: class Solution: def solveSudoku(self, board: List[List[str]]) -> None: \"\"\" Do not return anything, modify board in-place instead. \"\"\" if board is None or len(board) == 0: return self.solve(board) def solve(self, board): for i in range(len(board)): for j in range(len(board)): # 循环遍历,找到空位置 if board[i][j] == '.': # 从1-9遍历 for k in range(1, 10): c = str(k) if self.isValid(board, i, j, c): # 判断c是否可以放在(i, j)上 # 如果在(i, j)上放数字c可行的话,先将c放上去 board[i][j] = c # 继续递归,看放上c后,后面的是否可行 if self.solve(board): return True # 放上c后可以解决整个数独则返回True else: board[i][j] = '.' # 不可行的话将c清空 return False # 1-9遍历完后还不行就返回False return True # 可以遍历完整个board就返回True def isValid(self, board, row, col, c): for i in range(9): if board[i][col] != '.' and board[i][col] == c: return False if board[row][i] != '.' and board[row][i] == c: return False if board[3 * (row // 3) + i // 3][3 * (col // 3) + i % 3] != '.' and board[3 * (row // 3) + i // 3][3 * (col // 3) + i % 3] == c: return False return True","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"算法","slug":"算法","permalink":"http://yoursite.com/tags/算法/"},{"name":"数据结构","slug":"数据结构","permalink":"http://yoursite.com/tags/数据结构/"}]},{"title":"数据结构与算法———队列与栈","slug":"数据结构与算法———队列与栈","date":"2020-06-04T07:02:44.000Z","updated":"2020-06-04T07:04:44.000Z","comments":true,"path":"2020/06/04/数据结构与算法———队列与栈/","link":"","permalink":"http://yoursite.com/2020/06/04/数据结构与算法———队列与栈/","excerpt":"","text":"一、队列队列(Queue)是一个数据集合,仅允许在列表的一端插入,在另一端删除。进行插入的一端称为“队尾”(rear),插入的动作称为入队;进行删除的一端称为“队头”(front),删除的动作称为出队。队列的性质是先进先出(FIFO,First-in-First-out)。下图为一个例子: 图1:队列 队列的实现方式:环形队列设队列的最大长度为maxsize,队头指针为front,队尾指针为rear。上图中,队列最大长度为5,初始空队列front和rear都指向0。 当front == rear时,队为空,如图(a) 当(rear + 1) % maxsize == 0,队满。对于图(d1)的情况front == rear,无法判断到底是队空还是队满,因此需要牺牲一个存储单元,如图(d2)。 出队时队头指针前进1,front = (front + 1) % maxsize。 入队时队尾指针前进1,tear = (tear + 1) % maxsize。 上面的式子中,都有对maxsize的取余,因为比如当指针达到最大值5时,再加1就会超过5,此时取余可以重新进入队列循环。 二、栈栈(stack)是一个数据集合,只能在一端进行插入和删除,其特点是后进先出(LIFO,lats-in-first-out)。栈的节本操作有进栈(push)、出栈(pop)、取栈顶(gettop)。其示意图如下所示: 图1:队列 对于python来说,使用列表即可实现栈。设列表为stack,其三个基本操作为: 入栈:stack.append() 出栈:stack.pop() 取栈顶:stack[-1] 三、例题1、有效的括号此题为leetcode第20题思路:使用栈,从给定的括号字符串第一个开始,依次往后,左半边括号入栈,遇到与之对应的右边吧括号出栈,直到最后若栈为空,则为有效表达,否则为无效表达。代码如下: class Solution: def isValid(self, s: str) -> bool: map = {')':'(', ']':'[', '}':'{'} stack = [] for char in s: # 如果是右括号 if char in map: top = stack.pop() if stack else '#' if map[char] != top: return False # 如果是左括号 else: stack.append(char) return not stack 2、用队列实现栈此题为leetcode第225题思路:使用两个队列可以实现栈,对于栈的push操作,可以由两个队列实现:对于pop操作,直接从q1弹出即可 from collections import deque class MyStack: def __init__(self): \"\"\" Initialize your data structure here. \"\"\" self.q1 = deque() self.q2 = deque() def push(self, x: int) -> None: \"\"\" Push element x onto stack. \"\"\" self.q2.append(x) while self.q1: self.q2.append(self.q1.popleft()) self.q1, self.q2 = self.q2, self.q1 def pop(self) -> int: \"\"\" Removes the element on top of the stack and returns that element. \"\"\" return self.q1.popleft() def top(self) -> int: \"\"\" Get the top element. \"\"\" return self.q1[0] def empty(self) -> bool: \"\"\" Returns whether the stack is empty. \"\"\" return len(self.q1) == 0 3、用栈实现队列此题为leetcode第232题思路:同样可以用两个栈实现队列,对于push操作: class MyQueue: def __init__(self): \"\"\" Initialize your data structure here. \"\"\" self.s1, self.s2 = [], [] def push(self, x: int) -> None: \"\"\" Push element x to the back of queue. \"\"\" while self.s1: self.s2.append(self.s1.pop()) self.s1.append(x) while self.s2: self.s1.append(self.s2.pop()) def pop(self) -> int: \"\"\" Removes the element from in front of queue and returns that element. \"\"\" return self.s1.pop() def peek(self) -> int: \"\"\" Get the front element. \"\"\" return self.s1[-1] def empty(self) -> bool: \"\"\" Returns whether the queue is empty. \"\"\" return len(self.s1) == 0","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"算法","slug":"算法","permalink":"http://yoursite.com/tags/算法/"},{"name":"数据结构","slug":"数据结构","permalink":"http://yoursite.com/tags/数据结构/"}]},{"title":"数据结构与算法——平衡二叉树","slug":"数据结构与算法——平衡二叉树","date":"2020-06-04T07:00:08.000Z","updated":"2020-06-04T07:01:58.000Z","comments":true,"path":"2020/06/04/数据结构与算法——平衡二叉树/","link":"","permalink":"http://yoursite.com/2020/06/04/数据结构与算法——平衡二叉树/","excerpt":"","text":"一、平衡二叉树什么是平衡二叉树: 每个节点的两个子树的高度不会相差超过1。普通树和平衡二叉树的对比:为什么要用到平衡二叉树: 在二叉搜索树里面,搜索操作的时间复杂度从$O(logN)$到$O(N)$不等,这是一个巨大的性能差异。而平衡的二叉搜索树的搜索复杂度为$O(logN)$。常见的平衡二叉树的实现: 红黑树、AVL树、伸展树、树堆 二、判断是否为平衡二叉树自底至顶递归: 做先序遍历,从底部开始,判断左右子树高度差,若是平衡二叉树则返回子树的最大高度,若不是则直接输出false。此题为leetcode第110题,代码如下: class Solution: def recur(self, root): # 越过叶子节点,返回高度0 if not root: return 0 left = self.recur(root.left) if left == -1: return -1 right = self.recur(root.right) if right == -1: return -1 # 如果高度差大于1则不是平衡二叉树,否则返回左右子树最大高度加1 return max(left, right) + 1 if abs(left - right) < 2 else -1 def isBalanced(self, root: TreeNode) -> bool: return self.recur(root) != -1 复杂度分析: 时间复杂度:$O(N)$,N 为树的节点数;最差情况下,需要递归遍历树的所有节点。 空间复杂度:$O(N)$,最差情况下(树退化为链表时),系统递归需要使用 $O(N)$ 的栈空间。","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"算法","slug":"算法","permalink":"http://yoursite.com/tags/算法/"},{"name":"数据结构","slug":"数据结构","permalink":"http://yoursite.com/tags/数据结构/"}]},{"title":"数据机构与算法——二叉搜索树","slug":"数据机构与算法——二叉搜索树","date":"2020-03-13T14:34:55.000Z","updated":"2020-06-04T06:52:42.000Z","comments":true,"path":"2020/03/13/数据机构与算法——二叉搜索树/","link":"","permalink":"http://yoursite.com/2020/03/13/数据机构与算法——二叉搜索树/","excerpt":"","text":"一、二叉搜索树二叉搜索树(BST)满足的性质: 每个节点中的值必须大于(或等于)存储在其左侧子树中的任何值。 每个节点中的值必须小于(或等于)存储在其右子树中的任何值。 一个二叉搜索树的例子如下所示: 图1:二叉搜索树举例 因为二叉树的中序遍历会得到一个递增的序列,因此在二叉搜索树里中序遍历比较常用。 给定一个二叉树,我们要判断它是否是二叉搜索树。二叉搜索树的特征比较明显,我们需要判断:(1)当前节点下,其左子树只包含小于当前节点的数;(2)其右子树只包含大于当前节点的数;(3)所有左子树右子树必须也是二叉搜索树。此题是leetcode的第98题,我们用递归、迭代、中序遍历三种方法实现。 【注意】直观来看,遍历每个节点,只要当前节点比左节点大比右节点小就行。但这样是不对的,因为我们还要右子树的所有节点都要比当前节点大,左子树的所有节点都要比当前节点小,刚才的做法无法保证这一点。比如下图,虽然根节点5小于右节点6,但右子树有个节点值为4,小于根节点,因此不是二叉搜索树。 图2:非二叉搜索树举例 这种情况下,在右子树里比较时,要有一个下界,这个子树里的值都不能小于这个下界,这个下界就是根节点的值。同理,根节点的值就是左子树的上界,所有值都不能大于这个上界。 1、递归# 递归判断是否为二叉搜索树 class Solution: def isValidBST(self, root: TreeNode) -> bool: # 上界初始化为无穷,下界初始化为负无穷 def helper(node, lower = float('-inf'), upper = float('inf')): if not node: return True val = node.val # 如果当前节点小于下界或大于上界,则不是BST if val <= lower or val >= upper: return False # 考察右子树是否有低于下界的 if not helper(node.right, val, upper): return False # 考察左子树是否有高于上界的 if not helper(node.left, lower, val): return False # 以上都满足,则是BST return True return helper(root) 复杂度分析: 时间复杂度:$O(N)$,每个节点访问了一次 空间复杂度:$O(N)$,跟进了整个树 2、迭代# 迭代法,深度优先搜索 class Solution: def isValidBST(self, root: TreeNode) -> bool: if not root: return True # 初始化栈 stack = [(root, float('-inf'), float('inf')), ] while stack: # 出栈 root, lower, upper = stack.pop() # 如果not root,说明此节点的父节点没有左孩子或右孩子,continue即可 if not root: continue val = root.val # 判断当前节点 if val <= lower or val >= upper: return False # 压栈右孩子 stack.append((root.right, val, upper)) # 压栈左孩子 stack.append((root.left, lower, val)) return True 复杂度分析: 时间复杂度:$O(N)$,每个节点访问了一次 空间复杂度:$O(N)$,跟进了整个树 3、中序遍历# 中序遍历 class Solution: def isValidBST(self, root: TreeNode) -> bool: stack, inorder = [], float('-inf') while stack or root: # 到最左边的叶子节点 while root: stack.append(root) root = root.left # 出栈 root = stack.pop() # 如果它小于它前一个节点的值,则不是BST(因为是中序遍历,是按照递增排的) if root.val <= inorder: return False # 若它大于前一个值,则将当前值赋值给inorder值 inorder = root.val root = root.right return True 复杂度分析: 时间复杂度:时间复杂度 : 最坏情况下(树为二叉搜索树或破坏条件的元素是最右叶结点)为$O(N)$ 空间复杂度:$O(N)$用于存储stack二、基本操作1、在树中搜索某个值在搜索二叉树中找到某个值并返回改节点,举例如下图所示。我们要找到值为4的节点,从根节点开始,它大于4,因此在左子树找。左子树的根节点小于4,因此再在其右子树找。此时当前节点值为4,返回该节点。此题对于leetcode的第700题。 图3:在二叉搜索树中实现搜索操作 递归实现 class Solution: def searchBST(self, root: TreeNode, val: int) -> TreeNode: # 如果给节点为空,或该节点值等于要找到的值,则返回改节点 if not root or root.val == val: return root # 若改节点值大于val,则在左子树找 if root.val > val: return self.searchBST(root.left,val) # 否则在右子树找 else: return self.searchBST(root.right,val) 复杂度分析: 时间复杂度:$O(H)$,其中H是树高。平均时间复杂度:$O(logN)$;最坏时间复杂度:$O(N)$ 空间复杂度:$O(H)$,递归栈的深度为H。平均情况下深度为$O(logN)$;最坏情况戏曲深度为$O(N)$ 迭代实现 class Solution: def searchBST(self, root: TreeNode, val: int) -> TreeNode: while root is not None and root.val != val: root = root.left if val < root.val else root.right return root 复杂度分析: 时间复杂度:$O(H)$,其中H是树高。平均时间复杂度:$O(logN)$;最坏时间复杂度:$O(N)$ 空间复杂度:$O(1)$,恒定的额外空间 2、插入操作在这里实现的二叉搜索树的插入操作,主要思想是为目标节点找出合适的叶节点位置,然后将该节点作为叶节点插入。举例如下图所示: 图4:在二叉搜索树中实现插入操作 在左边这个树里插入4,从根节点开始,4小于5,因此继续搜索左子树。4大于2,而且2没有右孩子,因此4作为2的右孩子插入到树中。此题对于leetcode的第701题。 **递归实现:** class Solution: def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode: if not root: return TreeNode(val) if val > root.val: # 在右子树插入 root.right = self.insertIntoBST(root.right, val) else: # 在左子树插入 root.left = self.insertIntoBST(root.left, val) return root 复杂度分析: - 时间复杂度:$O(H)$,其中H是树高。平均时间复杂度:$O(logN)$;最坏时间复杂度:$O(N)$ - 空间复杂度:平均情况下 $O(H)$。最坏的情况下是 $O(N)$,是在递归过程中堆栈使用的空间。 **迭代实现:** class Solution: def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode: node = root while node: # 如果val大于当前节点的值,则在右子树插入 if val > node.val: # 如果没有右孩子,则插入 if not node.right: node.right = TreeNode(val) return root else: node = node.right # 如果val大于当前节点的值,则在左子树插入 else: # 如果没有左孩子,则插入 if not node.left: node.left = TreeNode(val) return root else: node = node.left return TreeNode(val) 复杂度分析: - 时间复杂度:$O(H)$,其中H是树高。平均时间复杂度:$O(logN)$;最坏时间复杂度:$O(N)$。 - 空间复杂度:$O(1)$ ## **3、删除操作** 这里的删除操作是用一个合适的子节点来替换要删除的目标节点,使整体操作变化最小。如果目标节点值大于当前节点,则指向右子树去寻找目标节点;如果目标节点值小于当前节点,则指向左子树去寻找目标节点。如果找到了目标节点,我们分为三种情况: - case1:目标节点没有子节点,可以直接删除目标节点。如下图所示: 图5:删除操作,情况1 case2:目标节点有右节点,则该节点可以由该节点的后继节点(successor,右子树最左边的节点)进行替代,该后继节点位于右子树中较低的位置。然后可以从后继节点的位置递归向下操作以删除后继节点。如下图所示: 图6:删除操作,情况2 case3:目标节点只有左节点,可以使用它的前驱节点(predecessor,左子树最右边的节点)进行替代,然后再递归的向下删除前驱节点。如下图所示: 图7:删除操作,情况3 此题对于leetcode的第450题。代码如下: class Solution: def successor(self, root): # 最左边的节点 root = root.right while root.left: root = root.left return root.val def predecessor(self, root): # 最右边的节点 root = root.left while root.right: root = root.right return root.val def deleteNode(self, root: TreeNode, key: int) -> TreeNode: if not root: return None # 从右子树删除 if key > root.val: root.right = self.deleteNode(root.right, key) # 从左子树删除 elif key < root.val: root.left = self.deleteNode(root.left, key) # 找到了要删除的节点 else: # case 1 if not (root.left or root.right): root = None # case 2 elif root.right: root.val = self.successor(root) root.right = self.deleteNode(root.right, root.val) # case 3 else: root.val = self.predecessor(root) root.left = self.deleteNode(root.left, root.val) return root 复杂度分析: 时间复杂度:时间复杂度:${O}(\\log N)$。在算法的执行过程中,我们一直在树上向左或向右移动。首先先用$O(H_{1})$的时间找到要删除的节点,$H_{1}$是从根节点到要删除节点的高度。然后删除节点需要 ${O}(H_2)$的时间,$H_{2}$指的是从要删除节点到替换节点的高度。由于 ${O}(H_1 + H_2) = {O}(H)$,$H$ 值得是树的高度,若树是一个平衡树则 $H = \\log N$。 空间复杂度:空间复杂度:$O(H)$,递归时堆栈使用的空间,HH 是树的高度 三、例题二叉搜索树的最近公共祖先 此题为leetcode第235题给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 递归写法: class Solution: def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': # 递归 # 当p、q的值都小于root时,要在左子树找 if p.val < root.val and q.val < root.val: return self.lowestCommonAncestor(root.left, p, q) # 当p、q的值都大于root时,要在右子树找 if p.val > root.val and q.val > root.val: return self.lowestCommonAncestor(root.right, p, q) # 否则p或q分列左右子树,root即为最近公共祖先 return root 非递归写法: class Solution: def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': # 非递归 while root: if p.val < root.val and q.val < root.val: root = root.left elif p.val > root.val and q.val > root.val: root = root.right else: return root","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"算法","slug":"算法","permalink":"http://yoursite.com/tags/算法/"},{"name":"数据结构","slug":"数据结构","permalink":"http://yoursite.com/tags/数据结构/"}]},{"title":"数据结构与算法——二叉树","slug":"数据结构与算法——二叉树","date":"2020-03-11T14:49:14.000Z","updated":"2020-06-04T06:38:58.000Z","comments":true,"path":"2020/03/11/数据结构与算法——二叉树/","link":"","permalink":"http://yoursite.com/2020/03/11/数据结构与算法——二叉树/","excerpt":"","text":"一、二叉树二叉树是一种更为典型的树树状结构。如它名字所描述的那样,二叉树是每个节点最多有两个子树的树结构,通常子树被称作“左子树”和“右子树”。下面是个二叉树的例子:用python定义二叉树的节点: # 二叉树节点 class TreeNode: def __init__(self, x): self.val = x self.left = None self.right = None 二、二叉树遍历1、前序遍历前序遍历访问的顺序为:根节点、左子树、右子树。对于上图,遍历过程如下: 一开始指向根节点,访问它,为E 有左子树,指向他并访问,为A A没有左子树,指向其右子树并访问,为C C有左子树和右子树,按顺序访问,为BD E的左半边访问完毕,指向E的右子树并访问,为G G只有右子树,访问它,为F F往下已经没有叶子节点,遍历结束 因此,前序遍历的结果为:EACBDGF 2、中序遍历中序遍历访问的顺序为:左子树、根节点、右子树。对于上图,遍历过程如下: 一开始指向根节点E,因为优先访问左子树,先看有没有左子树。发现根节点有左子树A,指向它 此时A有没有左子树,因此访问当前节点,为A A有右子树,指向其右子树C 此节点C有左子树,指向其左子树B 此时节点没有子树,访问其节点为B 然后往回指向节点B的父节点,并访问,为C C有右子树,访问,为D 此时指回根节点E,访问它,为E E有右子树,指向右子树G 因为G没有左子树,因此访问当前节点,为G G有右子树,访问,为F F无子树,遍历结束 因此,中序遍历的结果为:ABCDEGF。通常来说,对于二叉搜索树,我们可以通过中序遍历得到一个递增的有序序列 3、后序遍历后序遍历访问的顺序为:左子树、右子树、根节点。对于上图,遍历过程如下: 一开始指向根节点E,先指向左子树A 要优先访问左子树右子树,A无左子树、有右子树,因此指向A的右子树C C有左子树,指向左子树B B无子树,访问为B 指回C,C有右子树,指向并访问,为D 指回BD的根节点C,访问为C 指回C的根节点,访问为A 指回A的根节点E,访问其右子树G G有右子树,访问为F 指回F的根节点,访问为G 指回G的根节点,访问为E E已为整个树的根节点,遍历结束 因此,后序遍历的结果为:BDCAFGE。当你删除树中的节点时,用到后序遍历。 也就是说,当你删除一个节点时,你将首先删除它的左节点和它的右边的节点,然后再删除节点本身。 4、层序遍历层序遍历就是逐层遍历树结构,下图展示了它的层次结构:二叉树的层序遍历即为广度优先搜索,该算法从一个根节点开始,首先访问节点本身。 然后遍历它的相邻节点,其次遍历它的二级邻节点、三级邻节点,以此类推。广度优先搜索需要用到队列。遍历过程如下: 初始化队列q=[],并将根节点E入队,为q=[E] q出队,为E E有左子树和右子树,两者入队,为q=[A, G] q出队,为A,此时将A的右子树入队,为q=[G, C] q出队,为G,此时G的右子树入队,为q=[C, F] q出队,为C,此时C的左子树、右子树入队,为q=[F, B, D] q出队,为F,此时q=[B, D] F没有子树,q继续出队,为B,此时q=[D] q出队,为D q为空,遍历结束 因此,层序遍历的结果为:EAGCFBD。 三、程序实现二叉树的前序、中序、后序和层序遍历,分别为leetcode的第144、94、145、102题,都可以用递归和迭代两种方法做。 1、递归实现对于前序、中序和后序来说,递归的现实非常简单,他们的实现区别是根节点访问的顺序不一样。代码如下: # 前序遍历递归 class Solution: def preorderTraversal(self, root: TreeNode) -> List[int]: if not root: return [] else: return [root.val] + self.preorderTraversal(root.left) + self.preorderTraversal(root.right) # 中序遍历递归 class Solution: def inorderTraversal(self, root: TreeNode) -> List[int]: if not root: return [] else: return self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right) # 后序遍历递归 class Solution: def postorderTraversal(self, root: TreeNode) -> List[int]: if not root: return [] else: return self.postorderTraversal(root.left) + self.postorderTraversal(root.right)+ [root.val] 可以看出不同的地方就是[root.val]的位置不一样,前序遍历[root.val]就在最一开始,中序遍历[root.val]则在中间,后序遍历[root.val]就在最后。整个迭代结构下图所示: 对于层序遍历的递归实现,我们不仅要输出层序遍历的序列,还要有每个元素属于哪个层次的信息,可以用列表嵌套的方式,比如之前的例子里,层序遍历表示为[[E], [AG], [CF], [BD]]。 # 层序遍历递归 class Solution: def levelOrder(self, root: TreeNode) -> List[List[int]]: levels = [] if not root: return levels def helper(node, level): # start the current level if len(levels) == level: levels.append([]) # append the current node value levels[level].append(node.val) # process child nodes for the next level if node.left: helper(node.left, level + 1) if node.right: helper(node.right, level + 1) helper(root, 0) return levels 我们设更节点的层级序号为0,依次往下为1、2、3……。leaves为保存结果的列表,每一层的结果又为一个列表,保存在leaves里,层级序号即为在leaves里的index,在第i层级,leaves里应该有i+1个列表,第i+1个列表为当前层级的节点。每个列表只append层级序号相同的节点。 2、迭代实现二叉树的迭代实现都需要用到栈。对于前序、中序、后序遍历,他们的迭代实现大同小异: # 前序遍历迭代 class Solution: def preorderTraversal(self, root: TreeNode) -> List[int]: if root is None: return [] stack, output = [root, ], [] while stack: root = stack.pop() if root is not None: output.append(root.val) if root.right is not None: stack.append(root.right) if root.left is not None: stack.append(root.left) return output # 中序遍历迭代 class Solution: def inorderTraversal(self, root: TreeNode) -> List[int]: if root is None: return [] stack, output= [], [] curr = root while curr or len(stack) > 0: while curr: stack.append(curr) curr = curr.left curr = stack.pop() output.append(curr.val) curr = curr.right return output # 后序遍历迭代 class Solution: def postorderTraversal(self, root: TreeNode) -> List[int]: if root is None: return [] stack, output = [root, ], [] while stack: root = stack.pop() output.append(root.val) if root.left is not None: stack.append(root.left) if root.right is not None: stack.append(root.right) return output[::-1] 对于前序遍历,先初始化一个有根节点的栈,然后进入循环,出栈根节点,访问它。然后依次入栈右子树、左子树(注意栈是先进后出)。则下次循环,会先出栈左子树,访问它,然后入栈它的右子树、左子树。这样根节点E的右子树会在其左子树遍历完后才会出栈。循环停止条件是栈为空。 对于中序遍历,因为要优先遍历左子树,循环之前初始化一个空栈,进入循环后,用一个内循环依次入栈左子树,直到叶子节点(也为一个左子树),然后退出内循环,出栈并访问。 对于后序遍历,和前序遍历的整体结构是一样的,唯一不同的是在循环里先入栈左子树,后入栈右子树,循环结束后将结果逆序。 对于层序遍历的迭代实现,循环之前初始化一个有根节点的队列,此时队列只有一个元素,进入循环后,出队并访问,然后将根节点的左右子树依次入队。下次循环,依次将上次队列里的元素出队,同队入队他们的左右子树。这样每次循环只出队当前层次的节点,并入队他们的左右子树,完成了层次之间的交换。这实际上是一种广度优先搜索。层序遍历迭代写法: # 层序遍历迭代,使用栈 class Solution: def levelOrder(self, root: TreeNode) -> List[List[int]]: if root is None: return [] q = [root] result = [] while q: res = [] for i in range(len(q)): node = q.pop(0) res.append(node.val) if node.left: q.append(node.left) if node.right: q.append(node.right) result.append(res) return result # 层序遍历迭代,使用队列 from collections import deque class Solution: def levelOrder(self, root: TreeNode) -> List[List[int]]: if root is None: return [] res = [] q = deque() q.append(root) while q: size = len(q) curr_level = [] for _ in range(size): node = q.popleft() curr_level.append(node.val) if node.left: q.append(node.left) if node.right: q.append(node.right) res.append(curr_level) return res 四、例题二叉树的最近公共祖先 此题为leetcode第236题 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 class Solution: def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': # 树为空返回None if root is None: return None # 找到了p或q if root == p or root == q: return root # 在root的左子树找p或q left = self.lowestCommonAncestor(root.left, p, q) # 在root的右子树找p或q right = self.lowestCommonAncestor(root.right, p, q) if left is None: # 如果左子树没有p或q,返回右孩子 return right elif right is None: # 如果右子树没有p或q,返回左孩子 return left else: # 如果分别在左右子树找到了p或q,那么root就是最近公共祖先 return root 五、总结 前序、中序、后序遍历的递归实现几乎一模一样,区别是根节点的访问位置不一样 前序、中序、后序的迭代实现需要借助栈 层序遍历的递归和迭代实现需要借助队列","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"算法","slug":"算法","permalink":"http://yoursite.com/tags/算法/"},{"name":"数据结构","slug":"数据结构","permalink":"http://yoursite.com/tags/数据结构/"}]},{"title":"MobileNet系列","slug":"MobileNet系列","date":"2019-12-13T11:55:48.000Z","updated":"2020-08-03T02:55:44.000Z","comments":true,"path":"2019/12/13/MobileNet系列/","link":"","permalink":"http://yoursite.com/2019/12/13/MobileNet系列/","excerpt":"","text":"MobileNet是用在移动端的轻量级CNN,本文简单介绍MobileNet V1到V3的版本。 MobileNet V1 主要特点:把卷积拆分为Depthwise和Pointwise两部分(深度可分离卷积Separable convolution),用步长为2的卷积代替池化。 Depthwise和Pointwise图解: 假设有$N \\times H \\times W \\times C$的输入,普通卷积是做$k$个3x3的卷积,且same padding,$stride=1$,输出为$N \\times H \\times W \\times k$。depthwise是将此输入分为$group=C$组,然后每组做一次卷积,相当于收集了每个channel的特征,输出依然是$N \\times H \\times W \\times C$。pointwise是做$k$个普通的1x1卷积,相当于收集了每个点的特征。depthwise+pointwise的输出也为$N \\times H \\times W \\times k$。 普通卷积和MobileNet卷积对比如下图所示。计算一下两者的参数量: 普通卷积为:$C \\times k \\times 3 \\times 3$ depthwise+pointwise:$C \\times 3 \\times 3 + C \\times k \\times 1 \\times 1$ 压缩率为$\\frac{depthwise+pointwise}{conv}=\\frac{1}{k} + \\frac{1}{3 \\times 3}$ 进一步压缩模型:引入了width multiplier,所有通道数乘以$\\alpha \\in (0,1]$(四舍五入),以降低模型的宽度。 MobileNet V2 主要特点:引入残差结构;采用linear bottenecks + inverted residual结构,先升维后降维;使用relu6(最大输出为6)激活函数,使模型在低精度计算下有更强的鲁棒性。 linear bottenecks + inverted residual结构如下图所示。 V2版本依然是使用depthwise和pointwise,不同的是在depthwise前加了一个1x1卷积来扩大通道数目扩张系数为$t$,即通道数目扩大$t$倍,以增加特征丰富性。在pointwise之后再加1x1卷积将通道数目压缩至原输入的数目。 V2版本去掉了第二个1x1卷积之后的激活函数,称为linear bottleneck。作者认为激活函数在高维空间能够有效地增加非线性,但在地位空间会破坏特征。 与残差模块的对比: V2网络结构: MobileNet V3 主要特点:引入SE(squeeze and excitation)结构;使用hard swish激活函数;头部卷积通道数量由32变为16;V2在预测部分使用了一个bottleneck结构来提取特征,而V3用两个1x1代替了这个操作;结构用NAS技术生成 SE轻量级注意力结构,如下图所示。在depthwise后加入SE模块,首先globalpool,然后1x1卷积将其通道压缩为原来的1/4,然后再1x1卷积扩回去,再乘以SE的输入。SE即提高了精度,同时还没有增加时间消耗。 尾部修改: hard swish激活函数如下所示。swish激活函数可以提高精度,但计算量比较大,作者用relu近似模拟,称为hard swish \\Bbb{swich} x=x \\cdot \\sigma(x) \\\\ \\Bbb{h-swish}[x]=x\\frac{ReLU6(x+3)}{6} v2头部卷积为32x3x3,作者发现可以改为16,保证了精度且降低了延时时间。 网络结构搜索,借鉴了MansNet和NetAdapt,这部分以后再详细补充。 网络结构: 参考文献[1] https://zhuanlan.zhihu.com/p/35405071 [2] https://blog.csdn.net/mzpmzk/article/details/82976871 [3] https://www.cnblogs.com/darkknightzh/p/9410540.html [4] https://blog.csdn.net/DL_wly/article/details/90168883 [5] https://blog.csdn.net/Chunfengyanyulove/article/details/91358187 [6] https://www.jianshu.com/p/9af2ae74ec04 [7] https://www.cnblogs.com/dengshunge/p/11334640.html","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"http://yoursite.com/tags/深度学习/"},{"name":"CNN","slug":"CNN","permalink":"http://yoursite.com/tags/CNN/"}]},{"title":"读AutoDL论文——SCARLET-NAS","slug":"读AutoDL论文——SCARLET-NAS","date":"2019-12-13T11:33:39.000Z","updated":"2019-12-13T11:43:10.000Z","comments":true,"path":"2019/12/13/读AutoDL论文——SCARLET-NAS/","link":"","permalink":"http://yoursite.com/2019/12/13/读AutoDL论文——SCARLET-NAS/","excerpt":"","text":"概述本文要解决的问题是在文献[1]和[2]中都提到过的跳跃连接聚集的问题。虽然跳跃连接可以使超网络的长度可以收缩,但它使超网络的训练便得不稳定,进而使评估模型变得困难。本文首先深入讨论了跳跃连接给训练带来的不稳定性,给出了造成这种现象的根本原因;然后提出了一种可学习的稳定子,使超网络在可变深度的情况下训练也变得稳定;最后利用这种稳定子在ImageNet上训练达到了76.9%的SOTA,且相比于Efficient-B0有更少的FLOPs。 本文使用了两个搜索空间: $S_{1}$:与ProxylessNAS类似,采用MobileNetV2作为它的backbone,包括19层,每层7种选择,一共$7^{19}$种可能。 $S_{2}$:在$S_{1}$的基础上,给每个inverted bottleneck一个squeeze-and-excitation的操作,类似于MnasNet,一共$13^{19}$种可能。 训练的不稳定性 作者用single-path的方式在$S_{1}$空间上搜索,发现跳跃连接带来了严重的训练不稳定,如图1蓝色标记所示,训练过程的方差非常大,且采样的子模型准确率也很低。作者计算了已训练好的超网络的第三层内不同choice blocks的余弦距离,绘制成热图如下图所示: 可以看出前6个inverted bottlenecks有非常相似的特征(颜色较深),但跳跃连接的特征相似度就很低(颜色较浅),平均余弦距离低于0.6。在训练过程中,除跳跃连接外其他操作带来的都是相似的特征图,但跳跃连接带来了更多的特征增强,破坏了现有特征图的相似性,使超网络的训练恶化。 方法作者解决这个问题的方法是,使用一个可学习的稳定子来代替无参的跳跃连接,以实现同其他choice blocks一样相似的特征,同时带稳定子的子模型必须和不带稳定子的子模型在表征能力上是等价的。满足上述要求的稳定子称为Equivariant Learnable Stabilizer(ELS),可用下式表示: f_{l+1}^{o}(x_{l}^{c_{l}})=f_{l+1}^{o}(f_{l}^{ELS}(x_{l}^{c_{l}})),\\forall o \\in \\{0,1,2,\\cdots,n-1\\}作者这里选择ELS函数为不带BN和激活函数的1x1卷积,关于其等价的证明可参考原文。 在搜索阶段作者多目标进化搜索,因为更加关注准确率和加乘数,并且移动端的开发更加防止欠拟合,作者对搜索目标进行了加权且设置了最大加乘数$madds_{max}$和最小准确率$acc_{min}$,最终的搜索过程描述为: \\begin{array}{cl}{\\max } & {\\{a c c(m),-\\operatorname{madds}(m), \\operatorname{params}(m)\\}} \\\\ {\\text {s.t.}} & {m \\in \\text { search space } S} \\\\ {} & {w_{a c c}+w_{m a d d s}+w_{\\text {params}}=1, \\forall w>=0} \\\\ {} & {a c c(m)>\\operatorname{acc}_{\\text {min}}, \\text {madds}(m)","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"http://yoursite.com/tags/深度学习/"},{"name":"自动化","slug":"自动化","permalink":"http://yoursite.com/tags/自动化/"},{"name":"NAS","slug":"NAS","permalink":"http://yoursite.com/tags/NAS/"},{"name":"AutoDL","slug":"AutoDL","permalink":"http://yoursite.com/tags/AutoDL/"}]},{"title":"AutoDL论文解读(七):基于one-shot的NAS","slug":"AutoDL论文解读(七):基于one-shot的NAS","date":"2019-10-21T09:09:41.000Z","updated":"2020-08-03T04:50:26.000Z","comments":true,"path":"2019/10/21/AutoDL论文解读(七):基于one-shot的NAS/","link":"","permalink":"http://yoursite.com/2019/10/21/AutoDL论文解读(七):基于one-shot的NAS/","excerpt":"","text":"自动化机器学习(AutoML)最近变得越来越火,是机器学习下个发展方向之一。其中的神经网络结构搜索(NAS)是其中重要的技术之一。人工设计网络需要丰富的经验和专业知识,神经网络有众多的超参数,导致其搜索空间巨大。NAS即是在此巨大的搜索空间里自动地找到最优的网络结构,实现深度学习的自动化。自2017年谷歌与MIT各自在ICLR上各自发表基于强化学习的NAS以来,已产出200多篇论文,仅2019年上半年就有100多篇论文。此系列文章将解读AutoDL领域的经典论文与方法,笔者也是刚接触这个领域,有理解错误的地方还请批评指正!此系列的文章列表:AutoDL论文解读(一):基于强化学习的开创性工作AutoDL论文解读(二):基于遗传算法的典型方法AutoDL论文解读(三):基于块搜索的NASAutoDL论文解读(四):权值共享的NASAutoDL论文解读(五):可微分方法的NASAutoDL论文解读(六):基于代理模型的NASAutoDL论文解读(七):基于one-shot的NAS 本篇介绍三篇论文:《SMASH: One-Shot Model Architecture Search through HyperNetworks》,《Understanding and Simplifying One-Shot Architecture Search》和《Single Path One-Shot Neural Architecture Search with Uniform Sampling》。 一、SMASH: One-Shot Model Architecture Search through HyperNetworks1、总览这篇论文作者通过训练一个辅助模型:超网络(HyperHet),去训练搜索过程中的候选模型,这个超网络动态地生成生成具有可变结构的主模型的权值。尽管这些生成的权重比固定的网络结构自由学习得到的权重更差,但是不同网络在早期训练中的相对性能(即与最优值的距离)为最优状态下性能提供了有意义的指导。作者同时开发了一个基于存储库(memory-back)读写的网络表示机制,来定义各种各样的网络结构。作者称此方法为SMASH(one-Shot Model Architecture Search through Hypernetworks) 2、利用超网络的one-shot结构搜索在SMASH中,我们的目标是根据一个网络的验证集性能对一组网络的性能进行排序,这个任务是通过超网络生成权重来完成的。在每个训练步,我们随机地采样一个网络结构,用超网络生成它的权重,然后训练这个网络。训练结束后,再随机采样一些网络,它们的权重是由超网络生成的,直接评估它们的性能。选择其中在验证集表现最好的网络,再正常地训练它的权重。SMASH由两个核心要点:(1)我们采样网络结构的方法;(2)给定抽样结构的权重的方法。对于第一点,作者提出了网络的存储库视图,允许将复杂的、分支的拓扑作为二值向量进行采样和编码。对于第二点,作者使用超网络,直接学习二值向量的结构编码到权重的映射。这里假设只要超网络生成了合理的权值,网络的验证集性能将与使用正常训练权值时的性能相关,而结构的差异是其性能变化的主要因素。 3、定义Memory-Bank为了能探索非常广阔搜索的空间,也为了能将网络结构简单地编码成向量输入到超网络中,作者提出了网络的存储库视图。这个方法将网络视为一组可以读写的存储库(初始的张量为0),每一层是一个操作,这个操作从存储库的子集中读取数据,修改数据,然后将它写入另一个存储库子集。对于一个单分支结构,网络有一个大的存储库,它在每个操作上对其进行读写(对于ResNet是相加)。DenseNet这样的分支架构从所有以前写入的存储库读取数据,并将数据写入空的存储库,而FractalNet有更复杂的读写模式,见下图:这里一个基本的网络结构包括多个block,在给定特征图下每个block有多个存储库,特征图大小依次减半。下采样是通过1x1卷积和平均池化完成的,1x1卷积和输出层的权重是自由学习到的,而不是超网络生成的。示意图如下所示:在采样结构时,每个block的存储库的的数目和通道数目是随机采样的。当定义一个block里的每层时,我们随机选择读写模式和作用在读取的数据上的操作。当从多个存储库读取读取时,我们沿通道这个轴连接数据,写入存储库时,将此数据与写入存储库中数据相加。在这篇论文中,作者只从一个block里的存储库中读写,虽然也可以从其他block里的存储库中读写。 每个操作包含一个1x1卷积(降低输入通道数目),然后接带非线性变换的可变数量的卷积,如上图(a)中所示,随机选择四个卷积中的哪个被激活,以及它们的过滤器大小、膨胀系数、组数和输出单元数。这里的1x1卷积是由超网络生成的(上面也有一个1x1卷积,那个是用来和池化一块来减小特征图高宽的,是自由学习到的,和这里的不一样)。为了确保可变深度,每个块都学习一个含4个卷积的集合,并在一个块内的所有操作中共享它。这里限制了最大的滤波器大小和输出单元的数目,当采样的操作使用的值小于其最大值时,我们简单地将张量切片至所需的大小。固定的变换卷积和输出层根据传入的非空存储库的数量使用相同的切片。 作者力求最小化学习到的静态参数的数量,将网络的大部分容量放在超网络中。这一目标的一个显著结果就是,我们只在下采样层和输出层之前使用BatchNorm,因为特定层的运行统计量很难动态生成。这里作者采用一个简化版的WeightNorm,每个生成的1 x1滤波器除以其欧几里得范数(而不是每个通道单单独规范化),用在固定结构的网络中仅导致小精度下降。操作中其他卷积是没有标准化的。 4、学习结构到权重的映射超网络是用来给另一个网络参数化权重的,那个网络就是主网络(main network)。对于参数是$H$的静态超网络,主网络的权重$W$是一个已学习到的嵌入$z$的函数(例如感知机),因此,学习到的权值的数量通常小于主网络权值的完整数量。对于动态超网络,权值$W$的生成取决于网络输入$x$,或者对于循环神经网络,则取决于当前输入$x_{t}$和先前的隐藏状态$h_{t-1}$。 基于主网络结构$c$的编码,作者提出了一个动态超网络取生成权重$W$,目标是学习一个映射$W=H(c)$,也就是说给定$c$使其接近最优权值$W$,这样就可以根据使用超级网络生成的权重,得到验证集误差,并对每个$c$进行排序。 这里的超网络是全卷积的,这样输出张量$W$的维数就会随着输入$c$的维数而变化,这样我们就得到了标准格式BCHW的4D张量,批量大小为1,因此没有输出元素是完全独立的。这允许我们通过增加$c$的宽度或长度去改变主网络的深度和宽度。在这样的设计下,$W$的每个空间维度的切片都对应于$c$的一个特定子集。描述操作的信息被嵌入到相应的$c$的切片的通道维度中。 以下图为例,如果一个操作从存储库1,2,4中读取数据,然后写入到2,4中,然后相应的$c$的切片的第一、二、四个通道被填入1(表示读),切片的第六、八通道填入1(表示写)。其他的操作在余下的通道中用同样1-hot的方式编码。我们只根据操作的输出单元的数量对$c$的宽度进行编码,所以没有与$W$的任何元素对应的$c$元素都是空的。上述方案的一个朴素实现是让$c$的大小等于$W$的大小,或者让超网络用上采用去产生更多的元素。但作者发现这样的效果很差,取而代之的是采用一种基于通道的权重压缩方案,该方案减小了$c$的大小,并使超级网络的表示能力与主要网络的表示能力成比例。作者使$c$的空间范围等于$W$大小的一部分$k$,在超网络的输出处放$k$个单元,然后将得到的$1\\times k \\times height \\times width$重塑成要求的$W$的大小。$k$定为$DN^{2}$,$N$是最小的存储库大小,$D$是一个“深度压缩”超参数,表示$W$的多少个切片对应于$c$的一个切片。 标准的2D CNN的输入是$x \\in \\Bbb R^{B \\times C \\times H \\times L}$,B,C,H,L分别是批大小、通道、高度、宽度。我们的嵌入张量是$c \\in \\Bbb R^{1 \\times (2M+d_{max}) \\times (N_{max}/N)^{2} \\times n_{ch}/D}$,M是一个block里存储库的最大数目,$d_{max}$是最大的卷积膨胀率,$n_{ch}$是主网络所有1x1卷积输入通道数目的综合。条件嵌入(conditional embedding)$c$是一个每层用来读写的存储库的独热编码,它有$2M+d_{max}$个通道,前M个通道表示从哪些存储库中读取,下M个通道表示写入到哪些存储库中,最后$d_{max}$个通道是3x3卷积里膨胀率的独热编码。宽度这个维度表示每层的单元数目,长度这个维度表示网络深度,即输入通道的总数。这里将批大小保持为1,这样就不会有信号完全独立地通过超级网络传播。 超网络有$4DN^{2}$个输出通道,因此超网络的输出是$W=H(c) \\in \\Bbb R^{1 \\times 4DN^{2} \\times (N_{max}/N^{2})\\times n_{ch}/D}$,然后重塑为$W \\in \\Bbb R^{N_{max} \\times 4N_{max}n_{ch} \\times 1 \\times 1}$。我们一次生成整个主网络的权值,允许超网络根据邻近层的权值预测给定层的权值。超网络的接受域表示给定一个层,网络可以向上或向下多远来预测给定层的参数。当我们遍历主网络时,我们根据传入通道的数量沿其第二轴切片,并根据给定层的宽度沿第一轴切片。 二、Understanding and Simplifying One-Shot Architecture Search1、总览这篇论文里作者也是认为模型之间的权重共享是个可行的方向,训练一个能够模拟搜索空间中任何架构的大型网络。一个简单的例子如下所示:上图中,在网络的某个位置,有3x3卷积、5x5卷积或者最大池化三个操作可以选择,不同于分别训练三个模型,我们训练一个包括了这三个操作的模型(one-shot model),然后在验证阶段,我们选择性地剔除其中两个操作的输出,保留使预测准确率最高的操作。一个更复杂的例子,在一个网络中可能在很多位置上包含了很多不同的操作选择,搜索空间是随着选择数目指数地增长,而one-shot模型的大小只随选择数目线性增长。相同的权重可以用来评估很多不同的结构,极大地降低了计算量。 作者提出了这样的疑问,为什么不同的结构可以共享一个权重集合?one-shot模型仅在搜索过程中对结构的性能排序,搜索结束后表现最好的结构会被从头重新训练,但即使这样,固定的权重集合在非常广泛的结构集合里工作的很好,这也是违反直觉的。作者在这篇论文中目的是理解权重共享在NAS中所扮演的角色。作者发现,对于达到好的搜索结果,超网络和强化学习控制器都不是必须的。为此作者训练了一个大的one-shot模型,包含了搜索空间里的每个可能的操作。然后剔除一些操作,测量其对模型预测准确率的影响。作者发现网络自动将其能力集中在对产生良好预测最有用的操作上。剔除不太重要的操作对模型的预测的影响很小,而剔除很重要的操作对模型预测的影响很大。实际上,可以通过观察网络结构在训练集中无标签样例的行为,来预测网络在验证集上的准确率。 2、搜索空间设计为one-shot设计搜索空间需要满足以下几个要求:(1)搜索空间需要足够大来捕捉到多样的候选结构;(2)one-shot模型产生的验证集准确率必须能预测独立模型训练产生的精度;(3)在有限的计算资源下,one-shot模型需要足够小。下图给出了一个搜索空间的例子:它合并了关于网络结构和应用在网络中不同位置的操作的重要决策。在训练过程中,one-shot模型包括了三个不同的输入,这些输入被连接在一起。在评估阶段,它可以通过剔除Input 1和Input 3来模拟一个一个仅包含Input 2的网络。更一般地说,我们可以使能或禁止任何输入连接的组合。这样,搜索空间可以随着传入的跨连接数目指数级地增长,而one-shot模型大小只线性地增长。连接操作后面总是连着一个1x1卷积,使得无论有多少传入的跨连接,输出的滤波器数目都是常量。然后one-shot模型在1x1卷积的输出上应用不同的操作,将结果相加。在评估阶段,我们移除一些操作。上图中有4种操作:3x3卷积、5x5卷积、最大池化和Identity,但只有5x5卷积操作留了下来。这样的方法被用在更大的模型上,如下图所示:这里的网络也是由多个一样的cell堆叠而成的,每个cell被分成了几个固定数目的choice block。一个choice block来自于前驱cell的输出或者同一cell的前驱choice block。这里作者设每个cell里有$N_{choice}=4$个choice block,每个choice block最多有5个可能的输入:两个来自前驱cell,三个来自同一cell的前驱choice block。每个choice block最多可以选择两个操作,可选择的操作有:identity,一对3x3可分离卷积,一对5x5可分离卷积,一对7x7可分离卷积,1x7卷积跟7x1卷积,最大池化,平均池化。 3、训练one-shot模型one-shot模型是个大型的网络,用带动量的SGD训练,为了保证特定架构的one-shot模型精度与独立模型精度之间的良好关系,作者考虑了以下几个方面: 互相适应的鲁棒性。如果直接训练整个one-shot模型,模型里的各部分会相互耦合。即使移除不重要的操作,也会造成模型预测精度的急剧下降,one-shot模型和独立模型之间的准确率的关系也会退化。为了解决这个问题,作者在训练one-shot模型时也包含了path dropout(我理解的是类似于dropout,起到了正则化的作用),对于每个batch,也随机地剔除一些操作。通过实验发现,一开始的时候不用path dropout,然后随着时间逐渐地增加dropout的几率,可以达到很好的效果。dropout的几率是$r^{1/k}$,$0<r<1$是模型的超参数,$k$是给定一个操作下进来路径的数目。然而dropout一个节点的所有输入的几率是常数,这里作者设为5%。在单个cell里,不同的操作使彼此独立地被剔除。如果一个模型包含多个cell,那在每个cell里通用的操作会被剔除。 训练模型的稳定性。作者一开尝试实验的时候发现one-shot的训练很不稳定,但是仔细地应用BN可以增加稳定性,作者使用了BN-ReLU-Conv这样的卷积顺序。在评估阶段要剔除某些操作,这会使每层batch的统计量改变,因为对于候选结构无法提前得知其batch统计量。因此批BN在评估时的应用方式与在训练时完全相同——动态计算batch的统计信息。作者还发现,训练one-shot模型时,如果在一个batch里对每个样本都dropout同样的操作,训练也会不稳定。因此对于不同的样本子集,作者dropout不同的操作:作者将一个batch的样本分成多个小batch(文中称为ghost batch),一个batch有1024个样本,分成32个ghost batch,每个有32个样本,每个ghost batch剔除不同的操作。 防止过度正则化。在训练期间,L2正则化只应用于当前结构在one-shot模型里所用到的那部分。如果不这样,那些经常被删除的层就会更加规范化。 4、评估和选择当one-shot模型训练好之后,我们由一个固定的概率分布独立地采样结构,然后在验证集上评估。作者注意到,随机采样也可以的用遗传算法或基于神经网络的强化学习代替。完成搜索之后,从头训练表现最好的结构,同时也可以扩展架构以增加其性能,也可以缩小架构以减少计算成本。作者在实验中,增加了过滤器的数量来扩展架构。 5、理解one-shot模型具体的实验细节、超参数和结果可参考原论文,这里讨论一下为什么固定的模型权重集合可以在不同的结构里共享。作者通过这篇论文实验和上篇的SMASH的实验发现,one-shot模型的准确率在30%至90%之间,或者10%至60%之间,但搜索到的独立模型的准确率在92%至94.5%之间,或者70%至75%之间,为什么one-shot模型的准确率之间相差这么多? 注意作者之前做了这样的假设:one-shot模型可以学习网络中哪些操作最有用,并在可用时依赖于这些操作,移除不重要的操作对模型预测的准确率有较小的影响,移除很重要的操作对模型预测的准确率有较大的影响。为了去验证这样的假设,作者采样了一批结构(参照结构),大部分操作没有被移除($r=10^{-8}$)。作者将这些参考结构的预测与从实际搜索空间中采样的操作较少的候选结构的预测进行了比较,比较是在来自训练集的批样本上进行的。如果假设是正确的,那么由性能最佳的模型做出的预测与使能网络中的所有操作时做出的预测相似。 我们使用对称的KL散度来量化候选结构的预测与参考结构的预测的不同程度。one-shot模型用交叉熵损失函数,其输出是概率分布$(p_{1},p_{2}, \\dots, p_{n})$,候选结构的输出概率分布为$(q_{1},q_{2}, \\dots, q_{n})$,KL散度为$D_{KL}(p||q)=\\sum_{i=1}^{n} p_{i} {\\rm log}\\frac{p_{i}}{q_{i}}$,对称KL散度为$D_{KL}(p||q)+D_{KL}(q||p)$。如果当前训练样本的分布几乎相同,则对称的KL散度将接近于0。相反,如果分布非常不同,对称的KL散度可以变化得得非常大。作者计算了64个随机样本的KL散度,并取了平均值。实验结果如下图所示:训练集测得的KL散度与验证集测得的预测准确率有很强的相关性。此外,KL散度的计算没有用到训练集的标签。这意味着,候选体系结构的预测与参考体系结构的预测越接近(其中,one-shot模型中的大多数操作都是使能的),在独立模型训练期间,它的性能通常就越高。权重共享迫使one-shot模型识别并集中于对生成良好预测最有用的操作。 作者接下来展示了KL散度随着时间变化的趋势。作者采样了六个不同的结构然后观察随着训练它的KL散度的变化情况,如下图所示:一开始他们的K离散度是低的,因为模型的预测一开始置信度比较低,每类会分配一个大致相等的概率。随着训练,不同结构的预测开始变的不同,这导致了KL散度的激增,在训练后期,网络中最有用的操作对模型的预测有了强大的影响,KL散度开始下降。 三、Single path one-shot neural architecture search with uniform sampling1、总览大多数搜索空间松弛化的方法里,结构分布是被连续地参数化了,这些参数在超网络训练时一同被联合优化,因此结构搜索是在优化的过程中进行,优化结束后从结构分布中采样最好的结构。但这存在两个问题:(1)超网络里的参数是深度耦合的;(2)联合优化进一步引入了结构参数和超网络权重之间的耦合。梯度下降方法天生的贪婪特征会在结构分布和超网络权重中引入偏差,这可能会误导结构搜索。 one-shot方法为结构搜索提供了一个新的方向,并且没有结构松弛化和分布参数。结构搜索问题从超网络训练中解耦出来,变成一个单独的步骤,它结合了嵌套优化和联合优化方法的优点。刚才介绍的两篇论文就是这样的one-shot方法,解决了上面提到的第二个问题,但并没有很好地解决第一个问题,超网络的权重仍然是耦合的。在《Understanding and Simplifying One-Shot Architecture Search》里提到,one-shot成果的关键是一个使用权重继承的结构的准确率应该对已经优化好的结构有预测性。因此,作者提出超网络训练应该是随机的,所有的结构都可以同时优化它们的权重。为了减少超网络的权重耦合,作者提出了一个简单的搜索空间,单路径超网络(single path supernet)。对于超网络训练,作者采用均匀采样,平等地对待所有结构,没有超参数。 2、NAS方法回顾我们用$\\mathcal A$结构搜索空间,这是一个有向无环图(DAG),一个搜索到的结构是DAG的子图$a \\in \\mathcal A$,记为$\\mathcal N(a,w)$,$w$为权重。NAS的目的是去解决两个有关联的问题,第一个是在标准深度学习里给定一个网络结构,优化其权重: \\begin{equation} w_{a}=\\underset{w}{\\operatorname{argmin}} \\mathcal{L}_{\\text {train }}(\\mathcal{N}(a, w)) \\end{equation} \\tag{1}$\\mathcal L_{train}(\\cdot)$是在训练集上的损失函数。第二个问题是结构优化,一般来说是通过验证集的准确率来寻找: \\begin{equation} a^{\\ast}=\\underset{a \\in \\mathcal A}{\\operatorname{argmin}} {\\rm ACC}_{\\text {val }}(\\mathcal{N}(a, w_{a})) \\tag{2} \\end{equation}${\\rm ACC}_{\\rm val}(\\cdot)$是验证集准确率。真实情况下会对网络的内存消耗、FLOPs、latency、功耗等有要求,这些取决于结构$a$、软件和硬件等,但和权重$w_{a}$无关,因此作者称为“结构限制”。一个典型的限制网络的latency不大于预设的budget: \\begin{equation} {\\rm Latency(a^{\\ast}) \\leq {\\rm Lat}_{\\rm max}} \\tag{3} \\end{equation}对于大多数方法来说,同时满足式(2)和式(3)是很有挑战性的。最近的NAS方法采用了权值共享的策略,结构搜索空间$\\mathcal A$被编码进一个超网络,记作$\\mathcal N(\\mathcal A, W)$,$W$是超网络的权重。超网络被训练一次,所有的结构都从$W$里直接继承,因此他们在相同的图节点上式共享权值的。大多权值共享的方法将离散的搜索空间转为连续的,$\\mathcal A$松弛化为$\\mathcal A(\\theta)$,$\\theta$为表示结构分布的连续化参数。注意新的空间包含了原始的搜索空间:$\\mathcal A \\subseteq \\mathcal A(\\theta)$。这样松弛化的好处是可以用梯度方法联合优化权重和结构分布参数,表示如下: \\begin{equation} \\left(\\theta^{*}, W_{\\theta^{*}}\\right)=\\underset{\\theta, W}{\\operatorname{argmin}} \\mathcal{L}_{t r a i n}(\\mathcal{N}(\\mathcal{A}(\\theta), W)) \\tag{4} \\end{equation}优化后,最好的结构$a^{\\ast}$从$\\mathcal A(\\theta)$采样得到,然后从$W_{\\theta^{\\ast}}$继承权值、微调。理论上这样做很合理,但优化式(4)是具有挑战性的。首先,在超网络里的图节点的权重是互相依赖的、深度耦合的,但从$W$中继承的权重解耦了,尚不明确为什么这样做是有效的。第二,联合训练结构参数$\\theta$和权值$W$进一步引入了耦合。满足结构限制也是困难的,一些工作使用精心设计的soft损失项来扩充式(4)中的损失函数$\\mathcal L_{train}$,但也很难满足像式(3)中的限制。作者总结了一下这部分提到的方法,连同作者提出的方法一同作了比较: 3、one-shot NAS 回顾通过上面的分析,耦合的结构搜索和权重优化是困难的,我们能否同时拥有问题解耦和权值共享的优点?这引出了叫做one-shot的方法(即上面解读的两篇论文),这两种方法依然训练一个超网络然后共享其中的参数,超网络训练和结构搜索是解耦成两个步骤,这既不同于嵌套式优化也不同于联合优化。首先,第一步优化超网络权重: W_{\\mathcal{A}}=\\underset{W}{\\operatorname{argmin}} \\mathcal{L}_{\\text {train }}(\\mathcal{N}(\\mathcal{A}, W)) \\tag{5}相比于式(4),搜索空间的连续化参数消失了,只有权重被优化。第二步,结构搜索表示为: a^{*}=\\underset{a \\in \\mathcal{A}}{\\operatorname{argmax}} \\mathrm{ACC}_{\\mathrm{val}}\\left(\\mathcal{N}\\left(a, W_{\\mathcal{A}}(a)\\right)\\right) \\tag{6}在搜索过程中,每个采样的结构$a$从继承$W_{\\mathcal A}$继承权重,记为$W_{\\mathcal A}(a)$。同式(1)、式(2)相比,式(6)的主要不同是,结构的权重是合理初始化的,估计$ACC_{val}(\\cdot)$只需推断,没有微调或重新训练。找到最优的结构$a^{\\ast}$后,通过微调来获得$w_{a^{\\ast}}$。这样的搜索也是灵活的,任何适当的搜索方法都可以,这里作者采用遗传算法。结构限制,式(3)的结构限制也被精确地满足。一旦训练好超网络,搜索可以用不同的结构限制(如100ms latency 和 200ms latency)在超网络上被重复很多次,之前的方法都没有这样的特性。但超网络的权重依然是耦合的。 4、单路径超网络和均匀采样将式(1)作为理想的情况,one-shot要求权重$W_{\\mathcal A}(a)$接近于最优权值$w_{a}$,近似的程度取决于训练损失$\\mathcal L_{train}(\\mathcal N(a, W_{\\mathcal A}(a)))$被优化的程度。这引出一个原则,超网络的权重$W_{\\mathcal A}$应该与搜索空间中的所有子结构的优化同时进行,如下式: W_{\\mathcal{A}}=\\underset{W}{\\operatorname{argmin}} \\mathbb{E}_{a \\sim \\Gamma(\\mathcal{A})}\\left[\\mathcal{L}_{\\text {train }}(\\mathcal{N}(a, W(a)))\\right] \\tag{7}$\\Gamma(\\mathcal A)$是$a \\in \\mathcal A$的先验分布。注意,式(7)是式(5)的实现,在每步优化中,结构$a$是随机采样的,只有权重$W(a)$是被更新的。从这个意义上说,超级网络本身不再是一个有效的网络,它表现为一个随机超网络(stochastic supernet)。 为了较少权重之间的互相适应,作者建议将搜索空间简化到极限,只包含但路径结构,这可以看做是path dropout策略的极端情况,每次只保留一条路径,其他全部drop,如下图所示:它包括一系列的choice block,每个包含几个choices,每次只有一个choice被调用。这里不包含任何dorpout参数或微调。先验分布$\\Gamma (\\mathcal A)$可能是重要的,作者发现均匀采样已经足够好了。 5、超网络和choice block和《Understanding and Simplifying One-Shot Architecture Search》中的一样,choice block用于构建随机结构,一个choice block包含多个结构选择。对这里的但路径超网络,每次只有一个choice被调用,通过采样所有的choice block来获得一条路径。这里作者设计了两种choice block: 通道数目搜索。这个choice block搜索一个卷积层的通道数目,它会预先分配一个最大通道数目的权重张量(max_c_out, max_c_in, ksize),在超网络训练过程中,系统随机选择当前输出通道数目$c_out$,然后从其中切片出张量子集$[:c_out, : c_in, :]$,如下图所示: 混合精度量化搜索。这个choice block用于搜索卷积层的权值和特征的量化精度。在超网络训练过程中,特征图的位宽和滤波器权值是随机选择的,如下图所示:6、进化结构搜索对于式(6)的结构搜索,前面两篇论文使用随机搜索,而作者使用遗传算法,算法伪代码如下图所示:对于所有的实验,种群大小$P=50$,最大迭代次数$\\mathcal T=20$,$k=10$,对于交叉,随机选择两个候选结构去交叉生成一个新的结构,对于变异,在每个choice block以0.1的概率从候选变异中随机选择一种变异,产生一个新的结构。在对结构进行推断之前,BN的统计量从训练集中随机选择子集样本重新计算,因为超网络的BN统计量一般来说并不适合于候选结构。 参考文献[1] Brock A , Lim T , Ritchie J M , et al. SMASH: One-Shot Model Architecture Search through HyperNetworks[J]. 2017.[2] Bender, Gabriel, et al. “Understanding and simplifying one-shot architecture search.” International Conference on Machine Learning. 2018.[3] Guo, Zichao, et al. “Single path one-shot neural architecture search with uniform sampling.” arXiv preprint arXiv:1904.00420 (2019).[4] https://www.jiqizhixin.com/articles/2019-04-02-8[5] 《深入理解AutoML和AutoDL》","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"http://yoursite.com/tags/深度学习/"},{"name":"自动化","slug":"自动化","permalink":"http://yoursite.com/tags/自动化/"},{"name":"NAS","slug":"NAS","permalink":"http://yoursite.com/tags/NAS/"},{"name":"AutoDL","slug":"AutoDL","permalink":"http://yoursite.com/tags/AutoDL/"}]},{"title":"AutoDL论文解读(六):基于代理模型的NAS","slug":"AutoDL论文解读(六):基于代理模型的NAS","date":"2019-10-15T12:42:19.000Z","updated":"2020-08-03T02:54:00.000Z","comments":true,"path":"2019/10/15/AutoDL论文解读(六):基于代理模型的NAS/","link":"","permalink":"http://yoursite.com/2019/10/15/AutoDL论文解读(六):基于代理模型的NAS/","excerpt":"","text":"自动化机器学习(AutoML)最近变得越来越火,是机器学习下个发展方向之一。其中的神经网络结构搜索(NAS)是其中重要的技术之一。人工设计网络需要丰富的经验和专业知识,神经网络有众多的超参数,导致其搜索空间巨大。NAS即是在此巨大的搜索空间里自动地找到最优的网络结构,实现深度学习的自动化。自2017年谷歌与MIT各自在ICLR上各自发表基于强化学习的NAS以来,已产出200多篇论文,仅2019年上半年就有100多篇论文。此系列文章将解读AutoDL领域的经典论文与方法,笔者也是刚接触这个领域,有理解错误的地方还请批评指正!此系列的文章列表:AutoDL论文解读(一):基于强化学习的开创性工作AutoDL论文解读(二):基于遗传算法的典型方法AutoDL论文解读(三):基于块搜索的NASAutoDL论文解读(四):权值共享的NASAutoDL论文解读(五):可微分方法的NASAutoDL论文解读(六):基于代理模型的NASAutoDL论文解读(七):基于one-shot的NAS 本篇讲述基于代理模型的渐进式搜索《Progressive Neural Architecture Search》。代理模型就是训练并利用一个计算成本较低的模型去模拟原本计算成本较高的那个模型的预测结果,当需要计算的大模型的数量较多时,可以避开很多计算。 一、Progressive Neural Architecture Search1、总览这篇论文也是搜寻卷积cell,每个cell包含B个block,每个block连接两个输入,然后cell堆叠起来组成整个网络,同NASNet(《Learning Transferable Architectures for Scalable Image Recognition》)里的基本一样。不过作者是从简单的浅层的cell开始,逐渐地搜索到复杂的cell,而不是每次预测固定大小的cell。在算法的第b次迭代,有K个候选的cell(每个cell有b个block),这些会被训练和评估。因为这个过程是非常费时的,作者在这里使用代理模型去预测候选cell的效果。将K个包含b个block的候选cell扩展成$K^{\\prime}>>K$个cell,每个cell有b+1个block。使用代理模型去预测他们的表现,然后选取最高的K个,训练并评估。直到b=B,达到允许的最大block数目。 2、搜索空间这里作者不再区分normal cell和reduction cell,不过如果步长为2,同样可以实现reduction cell的功能。cell是由B个block组成的,每个block将两个输入组合成一个输出。一个在第$c$个cell里的第$b$个block 可以用一个元组表示$(I_{1},I_{2},O_{1},O_{2})$,这里$I_{1},I_{2} \\in {\\mathcal I}_{b}$表示输入,$O_{1},O_{2} \\in {\\mathcal O}_{b}$表示作用在输入$I_{i}$上的操作。对于两个输入的连接方式,NASNet里有相加和沿深度轴连接两种选择,不过作者发现沿深度轴连接在实验中从来没有被选择过,因此这里只将两个输入相加,生成block的输出$H_{b}^{c}$。可能的输入集合$\\mathcal I_{b}$是这个cell里所有的前驱block的输出$\\{H_{1}^{c}, \\dots, H_{b-1}^{c}\\}$,加上前一个cell的输出$H_{B}^{c-1}$,再加上前前一个cell的输出$H_{B}^{c-2}$。操作空间$\\mathcal O$包括以下几种:令第$b$个block可能的结构空间是$\\mathcal B_{b}$,那么这个空间的大小为$|\\mathcal B_{b}|=|\\mathcal I_{b}|^{2} \\times |\\mathcal O|^{2}$,$|\\mathcal I_{b}|=(2+b-1)$,$|\\mathcal O|=8$。对于$b=1$,只有$\\mathcal I_{1}=\\{H_{B}^{c-1}, H_{B}^{c-2}\\}$,所以总共有$|\\mathcal B|_{1}=4 \\times 4 \\times 8^{2}=256$个可能的cell。这里作者令$B=5$,那么一种有$\\left|\\mathcal{B}_{1: 5}\\right|=(2^{2} \\times 8^{2}) \\times (3^{2} \\times 8^{2}) \\times (4^{2} \\times 8^{2}) \\times (5^{2} \\times 8^{2} )\\times (6^{2} \\times 8^{2})=5.6 \\times 10^{14}$种可能。但这些可能的cell里有一些对称的结构,例如b=1时只有136个独一无二的cell,那么整个搜索空间大小大概是$10^{12}$数量级,比NASNet的$10^{28}$小了不少。 下图展示了可能的cell结构和cell的堆叠方式: 3、渐进式搜索作者从最简单的模型开始,按渐进的方式搜索。首先从只含一个block的cell开始,构造出所有可能的cell空间$\\mathcal B_{1}$,将它们放入队列中,并且训练、评估队列中所有的模型。然后扩展其中的每个cell至含有2个block,所有可能的cell空间为$\\mathcal B_{2}$,此时所有的候选cell有$|\\mathcal B_{1}| \\times |\\mathcal B_{2}|=256 \\times ((2+1)^{2} \\times 8^{2})=147,456$种可能。训练和评估这么多种模型是基本不可行的,因此作者使用预测函数(即代理模型)去预测这些模型的表现。之前已经训练评估过一些模型了,那么代理模型就从这些已经评估出来的模型和结果中训练。然后去预测上面147,456种可能的模型,选出K个效果最好的模型,将它们放入队列中,然后重复整个过程,直到cell包含B个block。具体算法如下图所示:下图展示了一个渐进式搜索的例子:这个例子设cell里最多有B=3个block,$\\mathcal B_{b}$表示有b个block时候选cell的集合。当cell包含一个block时,$\\mathcal S_{1}= \\mathcal B_{1}$,训练这些并评估这些cell,然后更新代理模型;在第二轮,将$\\mathcal S_{1}$里的每个cell扩展到2个block,此时$\\mathcal S_{2}^{\\prime}=\\mathcal B_{1:2}$,预测它们的性能,选择其中最高的$K$个组成$\\mathcal S_{2}$,训练并评估它们,然后更新代理模型;在第三轮,将$\\mathcal S_{2}$里的每个cell扩展为3个block,得到有3个block的cell的集合的子集$S_{3}^{\\prime} \\subseteq \\mathcal B_{1:3}$(在第二轮里已经选择了K个最好的cell,已经是子集了,再扩展地话依然是子集),预测它们的性能,选择最好的K个,训练评估它们,最后得到最好的模型。 4、用代理模型预测我们需要一个代理模型去预测模型的性能,这样的代理模型三友三个特点:(1)能处理可变长度的输入。即使代理模型是在仅包含b个block的cell上训练的,那它也得能预测包含b+1个block的cell的性能;(2)与真实性能呈相关关系。我们并不一定非要获得较低的均方误差,但我们确实希望代理模型预测的性能排序同真实的大致相同;(3)样本效率,我们希望训练和评估尽可能少的cell,这意味着用于代理模型的训练数据将是稀缺的。 作者尝试了两种代理模型:LSTM和MLP。对于LSTM,将长度为$4b$(表示每个block的$I_{1},I_{2},O_{1},O_{2}$)的序列作为输入,然后预测验证集准确率,使用L1损失训练模型。作者对$I_{1},I_{2} \\in \\mathcal I$使用$D$维的嵌入,对于$O_{1},O_{2} \\in \\mathcal O$使用另一套嵌入。对于MLP,作者将cell转为固定长度的向量:将每个block的操作嵌入成$D$维向量,连接block里的操作为$4D$向量,对于cell里所有block的$4D$维向量取平均。对于训练代理模型,因为样本量比较少,作者训练5个模型做集成。 5、实现细节对MLP代理模型,嵌入向量长度为100,使用两个全连接层,每层有100个神经元。对于LSTM代理模型,隐藏状态长度和嵌入向量长度为100。嵌入向量从[-0.1, 0.1]之间均匀采样初始化。最后全连接层的偏置设为1.8(sigmoid之后为0.68),这是b=1的所有模型的平均准确率。在搜索过程中,每个阶段评估K=256个模型(在第一阶段评估136个),cell最多有B=5个block,第一个卷积cell里有F=24个滤波器,在最终的网络结构里重复N=2次,训练20轮。 参考文献[1] Liu C , Zoph B , Neumann M , et al. Progressive Neural Architecture Search[J]. 2017.[2] 《深入理解AutoML和AutoDL》","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"http://yoursite.com/tags/深度学习/"},{"name":"自动化","slug":"自动化","permalink":"http://yoursite.com/tags/自动化/"},{"name":"NAS","slug":"NAS","permalink":"http://yoursite.com/tags/NAS/"},{"name":"AutoDL","slug":"AutoDL","permalink":"http://yoursite.com/tags/AutoDL/"}]},{"title":"AutoDL论文解读(五):可微分方法的NAS","slug":"AutoDL论文解读(五):可微分方法的NAS","date":"2019-10-15T12:40:44.000Z","updated":"2020-08-03T02:54:44.000Z","comments":true,"path":"2019/10/15/AutoDL论文解读(五):可微分方法的NAS/","link":"","permalink":"http://yoursite.com/2019/10/15/AutoDL论文解读(五):可微分方法的NAS/","excerpt":"","text":"自动化机器学习(AutoML)最近变得越来越火,是机器学习下个发展方向之一。其中的神经网络结构搜索(NAS)是其中重要的技术之一。人工设计网络需要丰富的经验和专业知识,神经网络有众多的超参数,导致其搜索空间巨大。NAS即是在此巨大的搜索空间里自动地找到最优的网络结构,实现深度学习的自动化。自2017年谷歌与MIT各自在ICLR上各自发表基于强化学习的NAS以来,已产出200多篇论文,仅2019年上半年就有100多篇论文。此系列文章将解读AutoDL领域的经典论文与方法,笔者也是刚接触这个领域,有理解错误的地方还请批评指正!此系列的文章列表:AutoDL论文解读(一):基于强化学习的开创性工作AutoDL论文解读(二):基于遗传算法的典型方法AutoDL论文解读(三):基于块搜索的NASAutoDL论文解读(四):权值共享的NASAutoDL论文解读(五):可微分方法的NASAutoDL论文解读(六):基于代理模型的NASAutoDL论文解读(七):基于one-shot的NAS 此篇博文介绍CMU的《DARTS:Differentiable Architecture Search》。之前介绍的NAS方法搜索空间都是离散的,而可微分方法将搜索空间松弛化使其变成连续的,则可以使用梯度的方法来解决。 一、DARTS:Differentiable Architecture Search1、搜索空间DARTS也是搜索卷积cell然后堆叠cell形成最终的网络。这里的cell是一个包含有向无环图,包含一个有$N$个节点的有序序列。每个节点$x^{(i)}$是一个隐含表示(比如特征图),每个有向的边$(i,j)$是变换$x^{(i)}$的操作$o^{(i,j)}$。作者假设cell有两个输入加点和一个输出节点,cell的输入输出设置和《Learning Transferable Architectures for Scalable Image Recognition》里的一致。每个中间节点是它所有的前驱节点计算得到: x^{(i)}=\\sum_{j","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"http://yoursite.com/tags/深度学习/"},{"name":"自动化","slug":"自动化","permalink":"http://yoursite.com/tags/自动化/"},{"name":"NAS","slug":"NAS","permalink":"http://yoursite.com/tags/NAS/"},{"name":"AutoDL","slug":"AutoDL","permalink":"http://yoursite.com/tags/AutoDL/"}]},{"title":"AutoDL论文解读(四):权值共享的搜索","slug":"AutoDL论文解读(四):权值共享的搜索","date":"2019-10-12T04:34:43.000Z","updated":"2020-08-03T02:54:36.000Z","comments":true,"path":"2019/10/12/AutoDL论文解读(四):权值共享的搜索/","link":"","permalink":"http://yoursite.com/2019/10/12/AutoDL论文解读(四):权值共享的搜索/","excerpt":"","text":"自动化机器学习(AutoML)最近变得越来越火,是机器学习下个发展方向之一。其中的神经网络结构搜索(NAS)是其中重要的技术之一。人工设计网络需要丰富的经验和专业知识,神经网络有众多的超参数,导致其搜索空间巨大。NAS即是在此巨大的搜索空间里自动地找到最优的网络结构,实现深度学习的自动化。自2017年谷歌与MIT各自在ICLR上各自发表基于强化学习的NAS以来,已产出200多篇论文,仅2019年上半年就有100多篇论文。此系列文章将解读AutoDL领域的经典论文与方法,笔者也是刚接触这个领域,有理解错误的地方还请批评指正!此系列的文章列表:AutoDL论文解读(一):基于强化学习的开创性工作AutoDL论文解读(二):基于遗传算法的典型方法AutoDL论文解读(三):基于块搜索的NASAutoDL论文解读(四):权值共享的NASAutoDL论文解读(五):可微分方法的NASAutoDL论文解读(六):基于代理模型的NASAutoDL论文解读(七):基于one-shot的NAS 本次介绍的两篇论文是谷歌的《Efficient Neural Architecture Search via Parameter Sharing》和自动化所、图森的《You Only Search Once: Single Shot Neural Architecture Search via Direct Sparse Optimization》。一般自动生成的网络模型庞大而冗余,且搜索过程耗时耗力,这次的论文是用权值共享的办法来加速模型搜索。 一、Efficient Neural Architecture Search via Parameter Sharing1、总览这篇论文也是基于NAS的框架,对其进行了改进,称之为ENAS。NAS搜索到的图是一个更大的图的子图,也就是说NAS的搜索空间可以用一个有向无环图(DAG)来表示。下图给出了一个例子:上面的图是整个搜索空间,红色的线连起来的子图是控制器发现的模型。ENAS的DAG是NAS搜索空间里所有可能子模型的叠加,图中的结点表示具体的计算操作,边表示信息流动。每个节点都有其自己的参数,这些参数在所有的子模型中都可以共享。其实ENAS要学习的就是节点之间的连线关系,通过不同的连线产生大量的模型。 2、生成RNN的cell为了生成RNN的cell,作者使用一个有N个节点的DAG。ENAS的控制器是个RNN,它决定两个方面:(1)哪个边被激活;(2)DAG中的结点选择哪种操作。原NAS论文里的RNN生成,cell的结构被固定为二叉树结构,只决定树上每个节点的操作。而这里ENAS即设计节点操作也设计cell的结构。以下图为例说明控制器生成cell的过程:假设有N=4个节点,$x_{t}$是cell的输入,$h_{t-1}$是前一个时间步的输出。采样过程如下: 在节点1:控制器采样一个激活函数。图中例子选择了tanh,那么节点1的计算为$h_{1}={\\rm tanh}(x_{t} \\cdot W^{x}+h_{t-1} \\cdot W_{1}^{h})$ 在节点2:控制器采样之前节点的编号和激活函数。例子中选择了编号1和ReLU,则节点2的计算为$h_{2}={\\rm ReLU}(h_{1} \\cdot W_{2,1}^{h})$ 在节点3:控制器再次采样之前节点的编号和激活函数。例子中选择了编号2和ReLU,则节点3的计算为:$h_{3}={\\rm ReLU}(h_{2} \\cdot (W_{3,2}^{h}))$ 在节点4:控制器再次采样之前节点的编号和激活函数。例子中选择了编号1和tanh,则节点3的计算为:$h_{4}={\\rm tanh}(h_{1} \\cdot (W_{4,2}^{h}))$ 对于输出,将那么没有被选做其他任何节点输入的结点取平均。例子中,节点3和节点4没有被选为其他节点的输入,那么$h_{t}=(h_{3}+h_{4})/2$ 在上面的例子中可以看出,对于每个节点对$j<l$都有独立的参数矩阵$W_{l,j}^{h}$,通过选择前面节点的彪悍,控制器也选择了使用哪个参数矩阵,因此搜索空间里所有的cell都共享同样的参数集合。如果一个cell有N个节点,选择4种激活函数(tahn,ReLU,identity,sigmoid),那么搜索空间有$4^{N} \\times N!$种配置。 3、训练ENAS、导出网络结构这里控制器LSTM有100个节点,通过softmax分类器进行采样。在ENAS中,有两类需要学习的参数:控制器LSTM的参数$\\theta$和子模型共享的参数$\\omega$。训练过程也包括两个阶段,第一个阶段用整个数据集训练$\\omega$,第二个阶段是训练$\\theta$,两个阶段轮流训练,具体细节如下: 训练$\\omega$。这步中,控制器的策略$\\pi(m;\\theta)$被固定,用SGD去最小化交叉熵损失函数${\\Bbb E}_{m ~ \\pi}[L(m_{i}, \\omega)]$,$m$是从$\\pi (m; \\theta)$中采样得到的一个模型。梯度使用蒙特卡洛估计: \\nabla_{\\omega} \\mathbb{E}_{\\mathbf{m} \\sim \\pi(\\mathbf{m} ; \\theta)}[\\mathcal{L}(\\mathbf{m} ; \\omega)] \\approx \\frac{1}{M} \\sum_{i=1}^{M} \\nabla_{\\omega} \\mathcal{L}\\left(\\mathbf{m}_{i}, \\omega\\right)上式是对梯度的无偏估计,有较大的方差。但作者发现当$M=1$的时候效果就比较好,也就是说使用从$\\pi (m; \\theta)$采样到的任意一个模型$m$上计算到的梯度就可以更新$\\omega$。 训练参数$\\theta$。在这步中固定$\\omega$来更新$\\theta$,以最大化期望奖励${\\Bbb E}_{m ~ \\pi(m; \\theta)}[\\mathcal R(m, \\omega)]$。奖励$\\mathcal R(m,\\omega)$是在验证集上计算得到的。 导出网络结构。首先从训练的策略$\\pi (m, \\theta)$采样几个模型,对于每个采样的模型,直接在验证集上计算奖励,然后选择奖励最高的模型从头训练。实际上这个过程会进行很多次,共享的权重也会在一段时间后更新。 4、设计卷积网络在生成CNN时,控制器作出两个方面的决定:(1)连接前面的那个些节点;(2)使用哪种操作。这两个决策构造了一个卷积层。选择连接到前面的那些节点可以用来产生跨连接,在第$k$层,前面最多有$k-1$个可选的结点,这会有$2^{k-1}$种可能的决策。举例如下图:上图中,在第$k=4$层,控制器选择了前面的第1、3个节点,所以第1、3层的输出在深度这个轴连接起来送到第4层。 对于可选的操作,一共有6种:3x3大小卷积、5x5大小卷积、3x3大小的可分离卷积、5x5大小可分离卷积、3x3的最大池化、3x3的平均池化。做这样的决策一共L次,可以得到一个L层的网络,在搜索空间里一共有$6^{L} \\times 2^{L(L-1)/2}$种网络。 5、设计卷积cell上述的搜索是从头搜索整个网络,也称作macro搜索。也可以只搜索卷积cell,然后将它们连接起来组成整个网络,这称为micro搜索。在每个cell里有B个节点,节点1和节点2作为cell的输入,是前两个cell的输出。对于剩下的B-2个节点,使用控制器取做两个决策:(1)选择两个前驱节点作为当前节点的输入;(2)选择两个操作作用在这两个前驱节点上。上述操作之后,将两个节点相加。可能的操作有5种:identity,3x3和5x5的可分离卷积,3x3的最大池化和平均池化。以B=4举例,如下图所示: 对于节点1、2,其作为输入,控制器不对此作决策。用$h_{1}$和$h_{2}$表示。 在节点3,控制器选择两个前驱节点和两种操作。这里选择了节点2和节点2,选择的操作为5x5可分离卷积和identity,所以$h_{3}={\\rm sep_conv_5x5}(h_{2})+{\\rm id}(h_2)$ 在节点4,控制器选择了节点3和节点1,选择的操作为3x3可分离卷积和3x3可分离卷积,所说$h_{4}={\\rm sep_conv_3x3}(h_{3})+{\\rm sep_conv_3x3}(h_{1})$ 最终除了$h_{4}$其他节点都作为了其他节点的输入,所以$h_{4}$作为cell的输出 从此搜索空间中还可以生成reduction cell,只需:(1)采样一个卷积cell;(2)应用步长为2的操作。卷积cell和reduction cell同时采样,则控制器RNN一共作$2(B-2)$种决策。在节点$i(3 \\leq i \\leq B)$,控制器从前$i-1$个节点任意选择2个节点,从5种操作中任意选择2个,所以总共有$(5 \\times (B-2))^{2}$种可能的cell,算上reduction cell,一共有$(5 \\times (B-2))^{4}$种可能的cell。 二、You Only Search Once: Single Shot Neural Architecture Search via Direct Sparse Optimization1、总览在这篇论文里,作者将NAS重新表述为从一个大型网络中剔除无用的连接,因此只需一个模型被训练。由于模型在训练阶段就直接被优化了,作者称之为直接稀疏优化NAS(Direct Sparse Optimization NAS, DSO-NAS)。作者进一步论证了这个稀疏正则化可以被改进的加速近端梯度(proximal gradient)方法优化,不需要任何的控制器、性能预测或者搜索空间松弛(搜索空间松弛是可微分方法的内容)。DSO-NAS也第一次论证了NAS可以直接在大型数据集上使用而没有块结构的共享。 2、MotivationDSO-NAS通用是根据神经网络的结构空间可以用有向无环图DAG表示,在此空间的任何结构都是其子图。也就是说一个特定的结构可以从整个图中选择节点和边来得到。这里作者用完整的图来表示单个block的搜索空间,整个网络是由这些block用跨连接堆叠起来。 对于一个有T个节点的DAG,第$i$个节点的输出$h^{(i)}$可以通过变换所有前驱节点输出$h^{(j)},j<i$的和来得到: h^{(i)}={\\mathcal O}^{(i)} \\left(\\sum_{j=1}^{i-1}h^{(j)} \\right)下图展示了DAG里的一个特定的block,节点表示操作$\\mathcal O$,边表示信息流动。上图中节点1和节点6是输入节点和输出节点,虚线表示没被连接起来的结点。那么节点5的输出是$h^{(5)}={\\mathcal O} ^{(5)}\\left(\\sum_{j=4}^{4}h^{(j)} \\right)$,也就是$h^{(5)}={\\mathcal O} ^{(5)}(h^{(2)} + h^{(4)})$ 然后结构搜索问题可以看作边裁剪问题。在搜索过程中,我们移除没有用的边和节点,留下最重要的结构。这里作者对每个节点的输出都乘上一个乘子,上式变成: h^{(i)}={\\mathcal O}^{(i)} \\left(\\sum_{j=1}^{i-1} \\lambda_{(j)}^{(i)} h^{(j)} \\right)$\\lambda_{(j)}^{(i)}$是作用在从节点$j$到节点$i$的乘子,然后对这些乘子使用稀疏正则化,使其在搜索过程中有些为0。如果$\\lambda_{(j)}^{(i)}$为0,则相关的边和节点就被移除了。 3、搜索空间DSO-NAS可以搜索block然后堆叠起来(micro-search),也可以搜索不带block的整个网络(macro-search)。 对于要搜索的block,设定里面有M个层级,每个层级包含N个不同的操作(也就是节点)。每个操作将其所有前层级的节点和block的输入连接起来,block的输出就是将block里的所有节点连接起来。对于每个连接,都乘上一个乘子$\\lambda$。对其使用稀疏正则化,优化之后移除$\\lambda$为0的连接和节点,得到最终的block结构。整个过程如下图所示:图(a)是完整的DAG,图(b)是优化每个边的乘子$\\lambda$,图(c)是移除了没有用的边和节点后最终的block。 第$b$个block里第$i$层级的第$j$个节点$h_{(b,i,j)}$可以用如下公式表示: h_{(b,i,j)}={\\mathcal O}_{(b,i,j)} \\left(\\sum_{m=1}^{i-1} \\sum_{n=1}^{N} \\lambda_{(b,m,n)}^{(i,j)} h_{(b,m,n)} + \\lambda_{(b,0,0)}^{(i,j)} O_{(b-1)} \\right)这里$h_{(b,0,0)}=O_{(b-1)}$和$h_{(b, M+1,0)}=O_{(b)}$分别是第$b$个block的输入节点和输出节点。第$m$个层级有$(m-1)N+1$个输入。第$b$个block的输出$O_{(b)}$是对所有连接到输出的结点应用reduction操作(1x1的卷积)$\\mathcal R$得到的: \\begin{aligned} O_{(b)}=\\mathcal{R}\\left(\\left[\\lambda_{(b, 1,1)}^{(M+1,0)} h_{(b, 1,1)}\\right], \\lambda_{(b, 1,2)}^{(M+1,0)} h_{(b, 1,2)}, \\ldots, \\lambda_{(b, m, n)}^{(M+1,0)} h_{(b, m, n)}, \\ldots \\lambda_{(b, M, N)}^{(M+1,0)} h_{(b, M, N)}\\right) \\\\+O_{(b-1)}, m \\in[1, M], n \\in[1, N] \\end{aligned}整个由block堆叠的完整网络如下图所示:整个网络有$S$个stage,每个stage有$B$个卷积block。除了最后一个stage,每个stage的最后都有一个reduction block。这里作者尝试了两种搜索空间:(1)$\\lambda$在所有block中都共享的搜索空间;(2)每个block中$\\lambda$都不同的搜索空间。 卷积操作遵循Conv-Bn-ReLU的顺序,使用下面几种操作: 3x3的可分离卷积 5x5的可分离卷积 3x3的平均池化 3x3的最大池化 对于reduction block,使用1x1和3x3大小的卷积核,步长为2。 搜索block的任务因此可以简化为学习每个边的$\\lambda$,可以用下式表示: \\min _{\\mathbf{W}, \\boldsymbol{\\lambda}} \\frac{1}{K} \\sum_{i=1}^{K} \\mathcal{L}\\left(\\mathbf{y}_{i}, N e t\\left(\\mathbf{x}_{i}, \\mathbf{W}, \\boldsymbol{\\lambda}\\right)\\right)+\\delta\\|\\mathbf{W}\\|_{F}^{2}+\\gamma\\|\\boldsymbol{\\lambda}\\|_{1}$\\mathbf{x}_{i}$和$\\mathbf{y}_{i}$是输入数据和标签,$K$表示训练样本的数目,$\\mathbf{W}$表示网络的权重,$\\delta$和$\\lambda$表示正则化权重。可以设${\\mathcal G}(\\lambda)=\\frac{1}{K} \\sum_{i=1}^{K} \\mathcal{L}\\left(\\mathbf{y}_{i},N e t\\left(\\mathbf{x}_{i}, \\mathbf{W}, \\boldsymbol{\\lambda}\\right)\\right)$ 4、优化、训练正则化参数$\\lambda$优化起来非常困难,尤其在有随机性的深度神经网络里,虽然启发式的阈值方法可行,但优化是不稳定的。不过使用稀疏结构选择(Sparse Structure Selection,SSS)可以解决这个问题,SSS通过修改理论上合理的优化方法加速近端梯度(Accelerated proximal Gradient,APG)方法,重写公式避免在计算梯度时重复地前向和反向计算: \\begin{array}{l}{\\mathbf{z}_{(t)}=\\boldsymbol{\\lambda}_{(t-1)}-\\eta_{(t)} \\nabla \\mathcal{G}\\left(\\boldsymbol{\\lambda}_{(t-1)}\\right)} \\\\ {\\mathbf{v}_{(t)}=S_{\\eta_{(t)} \\gamma}\\left(\\mathbf{z}_{(t)}\\right)-\\boldsymbol{\\lambda}_{(t-1)}+\\mu_{(t-1)} \\mathbf{v}_{(t-1)}} \\\\ {\\boldsymbol{\\lambda}_{(t)}=\\mathcal{S}_{\\eta_{(t)} \\gamma}\\left(\\mathbf{z}_{(t)}\\right)+\\mu_{(t)} \\mathbf{v}_{(t)}}\\end{array}$t$是迭代地轮数,${\\mathcal S}_{\\eta_{(t)} \\gamma}$表示软阈值操作${\\mathcal S}_{\\alpha}(\\mathbf{z})_{i}={\\rm sign}(z_{i})(|z_{i}|-\\alpha)_{+}$,${\\eta}_{(t)}$表示梯度步长,$\\mu$是动量大小。这种方法称之为APG-NAG。$\\mathbf{W}$和$\\mathbf{\\lambda}$使用NAG(Nesterov Accelerated Gradient )和APG-NAG同时被更新。然而APG-NAG不能直接用在这里,裁剪的搜索空间是有限的,会导致一定程度的过拟合。这里作者将训练数据分成两部分,一部分用来更新$\\mathbf{W}$,另一部分用来更新$\\mathbf{\\lambda}$。 整个方法包括三个阶段: 训练DAG全部连接的网络,得到好的权重初始化 从预训练的模型中搜索结构 从头训练最终的网络 在前两个阶段,BN层的参数被固定为1,为了避免影响$\\mathbf{\\lambda}$的学习。在第二步之后,作者通过一个全局宽度乘法器来调整每个操作中的滤波器数量,以满足计算预算。 参考文献[1] Pham H, Guan M Y, Zoph B, et al. Efficient Neural Architecture Search via Parameter Sharing[J]. 2018.[2] Zhang X , Huang Z , Wang N . You Only Search Once: Single Shot Neural Architecture Search via Direct Sparse Optimization[J]. 2018.[3] 你想要的神经网络自动设计,谷歌大脑帮你实现了:用参数共享高效地搜索神经网络架构(ENAS)[4] https://blog.csdn.net/weixin_34319817/article/details/89581856[5] Huang Z , Wang N . Data-Driven Sparse Structure Selection for Deep Neural Networks[J]. 2017.[6] 《深入理解AutoML和AutoDL》","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"http://yoursite.com/tags/深度学习/"},{"name":"自动化","slug":"自动化","permalink":"http://yoursite.com/tags/自动化/"},{"name":"NAS","slug":"NAS","permalink":"http://yoursite.com/tags/NAS/"},{"name":"AutoDL","slug":"AutoDL","permalink":"http://yoursite.com/tags/AutoDL/"}]},{"title":"AutoDL论文解读(三):基于层或块的搜索","slug":"AutoDL论文解读(三):基于层或块的搜索","date":"2019-10-10T10:40:28.000Z","updated":"2020-08-03T02:54:24.000Z","comments":true,"path":"2019/10/10/AutoDL论文解读(三):基于层或块的搜索/","link":"","permalink":"http://yoursite.com/2019/10/10/AutoDL论文解读(三):基于层或块的搜索/","excerpt":"","text":"自动化机器学习(AutoML)最近变得越来越火,是机器学习下个发展方向之一。其中的神经网络结构搜索(NAS)是其中重要的技术之一。人工设计网络需要丰富的经验和专业知识,神经网络有众多的超参数,导致其搜索空间巨大。NAS即是在此巨大的搜索空间里自动地找到最优的网络结构,实现深度学习的自动化。自2017年谷歌与MIT各自在ICLR上各自发表基于强化学习的NAS以来,已产出200多篇论文,仅2019年上半年就有100多篇论文。此系列文章将解读AutoDL领域的经典论文与方法,笔者也是刚接触这个领域,有理解错误的地方还请批评指正!此系列的文章列表:AutoDL论文解读(一):基于强化学习的开创性工作AutoDL论文解读(二):基于遗传算法的典型方法AutoDL论文解读(三):基于块搜索的NASAutoDL论文解读(四):权值共享的NASAutoDL论文解读(五):可微分方法的NASAutoDL论文解读(六):基于代理模型的NASAutoDL论文解读(七):基于one-shot的NAS 本篇介绍两篇论文:谷歌的《Learning Transferable Architectures for Scalable Image Recognition》,商汤的《Practical Block-wise Neural Network Architecture Generation》。前两篇博文介绍的方法都是从最简单的初始化网络一点一点地增加基本操作如卷积、池化等,这样搜索空间巨大非常耗时。这里介绍的两篇论文是以搜索基本的层结构或块结构为主要目的,然后重复堆叠这些基本结构组成最终的网络。这相当于增加了先验知识,也较少了搜索空间,速度会比之前的方法快。 一、Learning Transferable Architectures for Scalable Image Recognition1、引言本文提出的方法叫做NASNet,是基于《Neural Architecture Search with Reinforcement Learning》(NAS)这篇论文的。直接用NAS在巨大的数据集上比如ImageNet是非常耗时的,因此作者提出现在小数据集上比如CIFAR-10上搜索,然后将搜索到的结构迁移至大数据及上。作者为了实现这种可迁移性,设计了新的搜索空间(称作NASNet搜索空间),使网络的复杂性与网络的深度和输入图像的大小无关。并且,搜索空间中的所有卷积网络都是由结构相同但权重不同的卷积层(或cell)组成的,搜索网络结构就相当于搜索最好的cell。搜索cell会比搜索整个结构更快,而且cell结构更容易泛化。 2、Normal cell 和 Reduction cell在此方法中,卷积网的整体架构是人工确定的。它们由重复多次的卷积cell组成,每个卷积cell具有相同的结构,但是不同的权重。用控制器RNN去预测卷积cell,然后将卷积cell按顺序堆叠,以处理任意大小的输入和过滤器的深度。这里作者指定两种类型的卷积cell:(1)Normal cell,输入和输出的特征图维度相同;(2)Reduction cell,输出特征图长宽减半。当降低特征图大小时,将输出中的过滤器数量增加一倍,以保持大致恒定的隐藏状态维数。 3、搜索步骤在搜索空间里,每个cell接受两个初始的隐藏状态$h_{i}$和$h_{i-1}$,他们是前面两个较低层中的两个cell的输出或输入图像。给定这两个隐藏状态,控制器RNN循环地预测对他们的操作,如下图所示: 在图左边是控制器RNN,会预测5种操作,灰色的两个框是选择哪两个隐藏状态,黄色的两个框是选择对两个隐藏状态的操作比如卷积或池化,绿色的框是选择连接两个隐藏状态的方式。右边是预测出来的一个例子,这样的一个结构称为block,然后连续预测B次,形成B个block,组成一个卷积cell。也就是说,一个卷积cell里有B个这样的block。RNN的预测步骤可以总结如下:下图是预测出来的两种cell结构,作者选择B=5,可以看出每个cell都由5个block组成:关于隐藏空间的选择,具体如下图所示:在最左边的图,初始会有两个隐藏状态H1和H2,RNN预测出一个block,H1和H2分别经过3X3 pool和3X3 conv再相加,得到H3,然后H1、H2和H3作为下一步预测的初始状态集合,从中选择两个初始状态作为下个block的输入。在中间这个图,选择了H3和H2,然后预测出block的结构,得到H4,连同H1、H2和H3作为下一步预测的初始状态集合,以此类推。为了使控制器RNN能预测出Normal cell 和 Reduction cell,作者让RNN总共有2X5XB个预测,前5XB个预测的是Normal cell,后5XB个预测的是Reduction cell。 4、搜索空间在上述的 step 3 和 step 4 中,RNN需要选择对隐藏状态的操作,这里预定义的操作包括如下几种: 在 step 5 中需要选择对两个隐藏状态的连接方式,这里包括两种:(1)元素相加;(2)沿深度这个轴组合两个隐藏状态。最后,在cell里没有被用到的隐藏状态沿深度这个轴组合起来作为cell的输出。 其他关于搜索空间的细节:所有的卷积都使用ReLU激活函数;为了保证卷积cell的形状匹配,必要情况下会插入1X1的卷积;所有深度可分离卷积都不采用批归一化和(或)深度分离卷积和元素操作之间的ReLU;所有的卷积遵循ReLU、卷积操作、批归一化这样的顺序;当一个可分离卷积作被选为操作时,可分离卷积被两次应用于隐藏状态,作者发现这可以提高整体性能。 5、训练不同于NAS方法,这里的控制器RNN是用近端策略优化(Proximal Policy Optimization,PPO)算法,PPO算法实现过程简单同时具有优秀的性能,使用PPO速度比较快且稳定。 在学习了卷积cell之后,可以探索几个超参数来构建给定任务的最终网络:(1)cell重复数N;(2)初始卷积cell中的滤波器数目。在选择了初始过滤器的数量之后,在步幅为2时将过滤器的数量增加一倍。作者定义了一个简单的符号来表示所有网络中的这两个参数,例如,4 @ 64,其中4和64分别表示网络倒数第二层的细胞重复数和过滤器数。 控制器RNN是一个一层的100个隐层单元的LSTM,有2X5XB个softmax预测。 在训练时,作者还使用了ScheduledDropPath策略,这是DropPath 策略的改进版本。在DropPath中,cell内的每条路径在训练过程中以一定的概率随机丢弃。而在ScheduledDropPath中,cell中的每条路径都以一个在训练过程中线性增加的概率被删除。作者发现DropPath并不能很好地用于NASNet,而ScheduledDropPath在实验中显著提高了NASNet的最终性能。 二、Practical Block-wise Neural Network Architecture Generation1、引言大多数的现代网络如Inception、残差网络等都是block堆叠的结构,作者也以生成block为基础,堆叠这些块来生成网络。这里的block和上篇论文里的block不太一样,这里的block结构上相当于上篇里的cell。作者使用Q-learning、经验回放、epsilon-贪婪策略等方法去生成block结构,然后生成整个网络。作者还提出了一种早停策略,以加快搜索收敛。重新设计了奖励函数,使早停的网络的准确率和充分训练的网络的准确率有正相关性。相当于用此奖励函数去预测最终的准确率。 2、Network Structure Code为了将网络表示成有向无环图,作者提出了一种新的层表示方法,称作network structure code(NSC),如下表所示: 每个block用一个5-D的NSC向量集合表示。表中index表示层的编号,$T= \\{1,2,3, \\dots , {\\rm Max LayerIndex} \\}$,每个block里有多个层,每个层有唯一有序的编号,每个block有MaxLayerIndex个层。Type表示层的类型,如表中所示1表示卷积层,2表示最大池化层,3表示平均池化层,等等。Kernel Size只在卷积层和池化层里有。Pred1和Pred2表示连接到当前层的层编号,$k= \\{1,2,3, \\dots , {\\rm Max LayerIndex-1} \\}$ ,如pred1=1表示编号为1的层连接到了当前层,若只有一个层连接到当前层,则pred2=0。NAS的例子可以看下图: 上图中每个层我用红色数字表上了号,和codes里的编号是一致的。以左图的codes为例,(1,4,0,0,0)表示编号为1,此层类型为4即Identity,没有kernel size,没有被连接的层(实际上它是此block的第一个层,默认pred1和pred2为0);(2,1,1,1,0)表示编号为2,此层类型为1即Convolution,kernel size为1,编号为1的层连接到此层;以此类推。 此外,block里所有没有后继连接的层都被连接起来作为输出。所有的Convolution都为Pre-activation Convolution Cell(PCC),即包括ReLU、卷积、批归一化三个部分。 2、Q-learning在这篇论文里,$s \\in S$表示当前层的NSC,是个5-D向量:{later index,layer type,kernel size,pred1,pred2}。动作$a \\in A$是下一个层的决策。状态转移的过程$(s_{t}, a(s_{t})) \\rightarrow (s_{t+1})$如下图所示:图(b)是沿图(a)中的红线生成的,block的结构可以看做一个NSCs序列的动作选择策略${\\mathcal T}_{a1:T}$,选择的过程是马尔科夫决策过程,基于在一个block中表现好的层在另一个层中也应该表现好的假设。为了找到最优结构,这里最大化所有搜索轨迹的期望奖励: R_{\\mathcal T}={\\Bbb E}_{P({\\mathcal T}_{a1:T})}[{\\Bbb R}]$\\Bbb R$是累积的奖励。使用贝尔曼方程取解这个最大化问题,这里给出了迭代解法: \\begin{aligned} Q\\left(s_{T}, a\\right) &=0 \\\\ Q\\left(s_{T-1}, a_{T}\\right) &=(1-\\alpha) Q\\left(s_{T-1}, a_{T}\\right)+\\alpha r_{T} \\\\ Q\\left(s_{t}, a\\right) &=(1-\\alpha) Q\\left(s_{t}, a\\right) +\\alpha\\left[r_{t}+\\gamma \\max _{a^{\\prime}} Q\\left(s_{t+1}, a^{\\prime}\\right)\\right], t \\in\\{1,2, \\ldots T-2\\} \\end{aligned}$\\alpha$是学习率,决定新信息所占的比例,$\\gamma$是折扣因子,决定长期奖励的重要性。$r_{t}$表示在当前状态$s_{t}$下的奖励,$s_{T}$是最终状态,也就是NSC里的Terminal层。$r_{T}$是针对$a_{T}$后整个网络训练收敛后验证集的准确率。由于奖励$r_{t}$不能在我们的任务中明确测量,我们使用下式来加速训练: r_{t}=\\frac{r_{T}}{T}而之前的一些工作将$r_{t}$设置成了0,这会使收敛变慢,这就是所谓的时间信用分配问题,这使得强化学习非常耗时。 3、早停策略按搜索block的方式生成网络确实可以有效地加速搜索,但整个搜索过程还是比较费时的。为了加速学习过程,作者使用了早停策略。早停策略可能会导致准确率比较低。如下图所示:图中黄色的线是使用早停的准确率,橘色的线是训练收敛后的准确率,使用早停的准确率要低很多。同时从图中发现,FLOPs、block的密度和最终准确率成负相关。因此,最终定义奖励函数为: \\begin{aligned} \\text {reward}=\\mathrm{ACC}_{\\text {Earlystop }} -\\mu \\log (\\text { FLOPs }) -\\rho \\log (\\text { Density }) \\end{aligned}FLOPs是block计算复杂度的估计,Density是block这个有向无环图中边数比上顶点数。 4、训练细节 epsilon-贪婪策略:这里作者根据下表来逐渐降低epsilon值,使智能体从探索平滑地过度到利用: 经验回放:在每一次迭代后存储验证集准确率和block的描述。在每一次训练迭代里,智能体从经验回放采样64个block以及对应的验证集准确率,更新Q值64次。 BlockQNN生成:在更新过程中,学习率$\\alpha=0.01$,折扣因子$\\gamma ==1$,奖励函数中的参数$\\mu =1, \\rho=8$。智能体一次采样64个NSC的集合作为一个mini-batch,一个block里的最大层编号为23。训练智能体178轮,也就是说一共会采样178*64=11392个block。对于每个生成的完整的网络,训练12轮。在得到一个最优的块结构后,我们用堆叠的块构建整个网络,并对网络进行训练直到收敛,以验证集准确率作为选择最优网络的标准。 参考文献[1] Zoph B, Vasudevan V, Shlens J, et al. Learning Transferable Architectures for Scalable Image Recognition[J]. 2017.[2] Zhong Z , Yang Z , Deng B , et al. BlockQNN: Efficient Block-wise Neural Network Architecture Generation[J]. 2018.[3] http://baijiahao.baidu.com/s?id=1600615825111400186&wfr=spider&for=pc[54https://wenku.baidu.com/view/eb78122d814d2b160b4e767f5acfa1c7aa008292.html","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"http://yoursite.com/tags/深度学习/"},{"name":"自动化","slug":"自动化","permalink":"http://yoursite.com/tags/自动化/"},{"name":"NAS","slug":"NAS","permalink":"http://yoursite.com/tags/NAS/"},{"name":"AutoDL","slug":"AutoDL","permalink":"http://yoursite.com/tags/AutoDL/"}]},{"title":"AutoDL论文解读(二):基于遗传算法的典型工作","slug":"AutoDL论文解读(二):基于遗传算法的典型工作","date":"2019-10-08T12:41:30.000Z","updated":"2020-08-03T10:30:30.000Z","comments":true,"path":"2019/10/08/AutoDL论文解读(二):基于遗传算法的典型工作/","link":"","permalink":"http://yoursite.com/2019/10/08/AutoDL论文解读(二):基于遗传算法的典型工作/","excerpt":"","text":"自动化机器学习(AutoML)最近变得越来越火,是机器学习下个发展方向之一。其中的神经网络结构搜索(NAS)是其中重要的技术之一。人工设计网络需要丰富的经验和专业知识,神经网络有众多的超参数,导致其搜索空间巨大。NAS即是在此巨大的搜索空间里自动地找到最优的网络结构,实现深度学习的自动化。自2017年谷歌与MIT各自在ICLR上各自发表基于强化学习的NAS以来,已产出200多篇论文,仅2019年上半年就有100多篇论文。此系列文章将解读AutoDL领域的经典论文与方法,笔者也是刚接触这个领域,有理解错误的地方还请批评指正!此系列的文章列表:AutoDL论文解读(一):基于强化学习的开创性工作AutoDL论文解读(二):基于遗传算法的典型方法AutoDL论文解读(三):基于块搜索的NASAutoDL论文解读(四):权值共享的NASAutoDL论文解读(五):可微分方法的NASAutoDL论文解读(六):基于代理模型的NASAutoDL论文解读(七):基于one-shot的NAS 本文解读两篇基于遗传算法的典型方法:谷歌的《Large-Scale Evolution of Image Classifiers》和坦佩雷理工大学的《Finding Better Topologies for Deep Convolutional Neural Network by Evolution》。过去基于遗传算法的网络结构搜索大多依赖先验知识,以残差块(residual block)、稠密块(dense block)等已被验证有效的块为基础进行组合拼接,而且要求每个个体都从头开始训练,对计算资源要求很高。这里要介绍的两篇论文要解决上面的问题,它们的核心思想都是尽可能少的使用先验知识,从node-level进行网络结构搜索。 一、遗传算法首先,作者进化出一个神经网络模型的种群,这个种群会包含很多不同的网络模型,其中每个模型即为一个个体。这个模型被训练后在验证集上的准确率为此个体的适应度评估。在每一个进化步中,worker(即计算机)从种群中随机选择两个个体,然后计算并比较其适应度。适应度较差的个体从种群中被移除,较好的个体留下来作为亲本进行下一步的繁殖。繁殖过程中,worker复制这个亲本,然后对其进行变异,即对其进行结构上的修改,修改后的模型称为后代。这个后代被训练然后得到在验证集上的准确率,最后被放回到种群里。用这样的策略在巨大的搜索空间里寻找最优的分类模型需要巨大的计算量,为此作者提出了一个大规模并行的方法。很多worker在不同的计算机上异步地操作,他们彼此之间不直接通信。他们使用一个共享的文件系统,这里用字典存储了种群的信息,每个字典代表一个个体。相应的操作可以通过修改字典完成,例如,移除一个个体即为字典的重命名。有时会出现这种情况,对于同一个个体,一个worker尝试对其进行修改,但另一个worker可能正在对其进行操作,此时这个worker放弃修改并重新尝试。在这篇论文里,种群的大小是1000个个体,worker的个数总是为种群大小的四分之一。为了在有限的空间内实现长时间的运行,会经常对被移除的个体的字典进行垃圾收集。 二、方法实现1、编码和变异个体的结构被编码为一个图,我们称之为DNA。在图中,顶点表示3阶张量或激活函数。3阶张量表示图片的长宽和通道数。激活函数可以是带ReLu的批标准化,也可以是简单的线性单元。图的边表示恒等映射或卷积,并包含定义卷积特性的变异了的数值参数。当一个顶点有多个边时,他们的长宽和通道数目可能不一致。为了解决这个问题,选择这些边中的一个作为主要边,这个主要边是非跨连接的。非主要边的激活值以主要边为基准,在长宽这个维度上进行zerothorder插值,在通道这个维度上进行填充或截断。除了图,学习率也被存在DNA里。后代是通过对亲本的复制和变异产生的,这些变异是从预先定义的集合里随机选择的。预先定义的变异集合包括: 改变学习率 恒等(即“保持训练”) 重置权重 插入卷积(在网络主干的随机位置上插入卷积,滤波器大小为3X3,步长为1或2,通道数目和输入相同,可能应用带ReLu的批归一化) 移除卷积 改变步长(仅允许2的幂的数值) 改变通道数目 滤波器大小(只对随机卷积,在水平或垂直方向上改变,只允许奇数值) 插入恒等连接 添加跨连接 移除跨连接 上述所有随时选取都是均匀分布的。对于数值型的变异,新的值在现有值附近选取。例如,一个变异作用于一个有10个输出通道的卷积,将会导致一个有5到20个输出通道的卷积(即是原始值的一半到两倍),在此范围内的值都是允许的。对于其他参数也是如此,从而产生一个密集的搜索空间。对于步长,都使用以2为底的log值,以便激活值的形状更容易匹配。原则上,参数没有上限,例如,模型的深度没有限制。参数的密集性和无界性会导致大量可能的网络结构。 2、初始情况初始化的种群里包含的都是简单的个体,这些个体都是没有卷积的单层模型,学习率为0.1,这样的个体表现是非常差的。这样的初始化迫使算法自己去大量地探索。这样表现差的初始化和巨大的搜索空间最大限度地减小了人为主观因素的影响。 3、训练和验证此论文代码使用Tensorflow实现,用momentum为0.9的SGD优化器,batch_size为50,衰减权重为0.0001,每次训练跑25600轮。在测试阶段,根据验证集准确率选择一些模型做集成。 4、计算代价为了估计计算代价,作者确定了模型训练和验证中使用的基本Tensorflow(TF)操作,如卷积、一般矩阵乘法等。对于每个TF操作,我们估计了所需的浮点操作(FLOPs)的理论数量。在实验中的每个个体,我们在一批样本上计算由TF操作引起的总FLOPs,包括训练阶段的($F_{t} {\\rm FLOPs}$)和验证阶段的($F_{v} {\\rm FLOPs}$)。然后计算$F_{t}N_{t}+F_{v}N_{v}$,$N_{t}$和$N_{v}$分别是训练和验证的batch数目。实验的总代价是其所有个体的代价之和。这里的FLOPs仅作为粗略的估计。这里没有考虑输入\\输出、数据预处理、TF计算图构建或内存复制的时间。 4、权重继承25600轮数对于训练一个大型网络来说还不够,在实验中每次都需要从头训练模型,这样会非常耗时。这里作者允许后代尽可能地继承亲本的参数权重,如果一个层的形状匹配,则保留其权重。因此,有些突变保留了所有的权值(比如恒等映射或学习率突变),有些则什么也没有保留(比如重置权重突变),而大多数则保留了部分但不是全部。 三、Finding Better Topologies for Deep Convolutional Neural Network by Evolution此篇论文方法和上一篇的比较相似,这里介绍其主要思路。 作者用一个有向图$G(N,E)$表示CNN,$N$是节点(node)集合,$E$是边(edge)集合。源节点(source node)表示输入,汇节点(sink node)表示输出,内节点(internal node)表示对前驱节点的和、激活、池化、卷积等操作,也称为卷积节点,其输出是特征图。每个卷积节点包括如下模型参数:激活函数、通道数和池化类型,默认全部采用ReLU激活函数。池化类型可以是None或最大池化(步长为2)。 进化从简单的拓扑结构开始,包括一个源节点、汇节点和一个卷积节点。每次繁殖,个体产生变异,变异是从预先定义好的集合里随机选择,变异集合里包括: 卷积节点通道数目加倍 在图上增加一个新的卷积节点。卷积核大小默认为3,如果被连接的两个节点的特征图大小不同,则选择合适的步长。 用新边连接两个节点。卷积核大小从1、3、5、7、9中随机选择。 裁剪边。 将节点插入到边。如果节点插入到了包含汇节点的边,那么在插入节点的前驱节点使用最大池化。插入节点的通道数和前驱节点的相同。 和Large-Scale Evolution的方法一样,这里采只使用了变异算子而没有使用交叉算子。之前的NEAT方法整合了有性繁殖,在父母的染色体上发生交叉。交叉是产生后代变异的重要手段,从而提高了后代在进化过程中的探索能力。然而,在自然界,有性生殖比无性生殖出现得晚得多。早期生物,特别是单细胞生物,都是无性繁殖的。这些生物的遗传特征很简单,少量的突变就足以引起变异。由于进化是从最简单的拓扑结构开始的,所以作者模仿了早期生物的进化,并应用了无性生殖。由于很难将两个不同的图合并以生成新的图,因此任何组合两个图的严格规则都会极大地限制搜索空间,而无性繁殖能够在整个拓扑空间中搜索。不过,后代可能可能和它的亲本很像,所以进化会比较缓慢。 如果一个变异产生的网络是无效的,例如源节点和汇节点没有连接或者图是循环的,则随机选择另一个变异。这个过程不断重复,直到生成一个有效的图。 在进化阶段,我们根据每个个体的验证集准确率来评估其适应度。然后使用随机排序比例选择策略,选择哪些个体留下来。个体能留下来的概率和它在整个种群的排序是成比例的,我们假设其服从玻尔兹曼分布,概率质量函数为: p(k)=\\frac{(1-e^{- \\lambda}) e^{- \\lambda k}}{1-e^{\\lambda N}}$k$是个体的排序,$N$是种群的大小,$\\lambda$是平衡“探索”和“利用”的参数。当$\\lambda$比较大时,$p(k)$变的平滑,适应度低的个体变的更容易被选择,算法更倾向于“探索”。 这篇论文也采用了权重继承来加速训练。后代直接继承亲本的卷积核权重,由于变异导致的不与亲本共享的部分则随机初始化。 本论文中还使用贝叶斯方法从种群中学习超参数,如学习率、批大小、dropout、优化器等。作者这些超参数当做阶学习的知识,不把其当做需要进化的部分,而是从种群中不断学习最优参数并传给下一代。具体算法如下所示: 参考文献[1] Real E, Moore S, Selle A, et al. Large-Scale Evolution of Image Classifiers[J]. 2017.[2] Zhang H, Kiranyaz S, Gabbouj M. Finding Better Topologies for Deep Convolutional Neural Networks by Evolution[J]. 2018.[3] 《深入理解AutoML和AutoDL》","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"http://yoursite.com/tags/深度学习/"},{"name":"自动化","slug":"自动化","permalink":"http://yoursite.com/tags/自动化/"},{"name":"NAS","slug":"NAS","permalink":"http://yoursite.com/tags/NAS/"},{"name":"AutoDL","slug":"AutoDL","permalink":"http://yoursite.com/tags/AutoDL/"}]},{"title":"AutoDL论文解读(一):基于强化学习的开创性工作","slug":"AutoDL论文解读(一):基于强化学习的开创性工作","date":"2019-09-30T08:06:00.000Z","updated":"2020-08-03T02:54:56.000Z","comments":true,"path":"2019/09/30/AutoDL论文解读(一):基于强化学习的开创性工作/","link":"","permalink":"http://yoursite.com/2019/09/30/AutoDL论文解读(一):基于强化学习的开创性工作/","excerpt":"","text":"自动化机器学习(AutoML)最近变得越来越火,是机器学习下个发展方向之一。其中的神经网络结构搜索(NAS)是其中重要的技术之一。人工设计网络需要丰富的经验和专业知识,神经网络有众多的超参数,导致其搜索空间巨大。NAS即是在此巨大的搜索空间里自动地找到最优的网络结构,实现深度学习的自动化。自2017年谷歌与MIT各自在ICLR上各自发表基于强化学习的NAS以来,已产出200多篇论文,仅2019年上半年就有100多篇论文。此系列文章将解读AutoDL领域的经典论文与方法,笔者也是刚接触这个领域,有理解错误的地方还请批评指正!此系列的文章列表:AutoDL论文解读(一):基于强化学习的开创性工作AutoDL论文解读(二):基于遗传算法的典型方法AutoDL论文解读(三):基于块搜索的NASAutoDL论文解读(四):权值共享的NASAutoDL论文解读(五):可微分方法的NASAutoDL论文解读(六):基于代理模型的NASAutoDL论文解读(七):基于one-shot的NAS 本文解读两篇基于强化学习的开创性论文:谷歌的《Neural Architecture Search with Reinforcement Learning》和MIT的《Designing Neural Network Architectures Using Reinforcement Learning》。用到的强化学习方法分别为策略梯度(Policy Gradient)和Q-learning。 一、强化学习相关背景知识强化学习任务包括量大主体:智能体(Agent)和环境(Environment),智能体通过与环境交互获得奖励,来学习相应的策略。强化学习有三种基本方法:动图规划、蒙特卡洛方法和时序差分方法,如下框图所示: 第一篇论文用到的方法是基于策略的策略梯度法,第二篇用到的方法是Q-learning。关于强化学习笔者会在另一篇文章中详细介绍。 二、Neural Architecture Search with Reinforcement Learning1、总览这篇论文的大体框架和思路如下图所示。在这篇论文中作者使用一个RNN作为智能体。作者考虑到神经网络的结构和连接可以用一个可变长度的字符串表示,这样可以用RNN生成这样的字符串。RNN采样生成了这样的字符串,一个网络,即子网络(child network)就被确定了。训练这个子网络,得到验证集在这个网络上的准确率,作为奖励信号。然后根据奖励计算策略梯度,更新RNN。在下一轮迭代,RNN会给出可能在验证集上准确率高的一个网络结构。也就是说,RNN会随着时间提升它的搜索质量。 2、控制器RNN的实现首先作者假设要生成一个前向的只包含卷积的网络(没有跨连接)。使用RNN可以生成卷积层的超参数,超参数表示为一个标记序列:RNN会预测每一层的滤波器的高度、宽度,步长的高度、宽度,和滤波器个数,一个5个参数。每一个预测是通过softmax分类器实现的,这个时间步的预测会作为下个时间步的输入。当层数达到一个特定的值,这个生成的过程就会停止。这个值会随着训练过程增加。一旦RNN生成了一个结构,即子网络,这个子网络就会被训练,验证集的准确率被记录下来被作为奖励。然后控制器RNN的参数$\\theta _{c}$被更新以生成期望验证集准确率最大的结构。 3、策略梯度RNN生成一个子网络后,其生成的标记序列可以被看作一系列动作$a_{1:T}$。验证集在其上取得的准确率作为奖励$R$,然后训练RNN。为了找到最优的结构,我们网RNN取最大化期望奖励$J(\\theta _{c})$: J(\\theta _{c})=E_{P(a_{1:T};\\theta_{c})}[R]但奖励信号是不可微分的,我们使用策略梯度算法去迭代地更新$J(\\theta _{c})$: \\nabla _{\\theta_{c}} J\\left(\\theta_{c}\\right)=\\sum_{t=1}^{T} E_{P\\left(a_{1 : T} ; \\theta_{c}\\right)}\\left[\\nabla \\theta_{c} \\log P\\left(a_{t} | a_{(t-1) : 1} ; \\theta_{c}\\right) R\\right]上式的经验估计如下: \\frac{1}{m} \\sum_{k=1}^{m} \\sum_{t=1}^{T} \\nabla_{\\theta_{c}} \\log P\\left(a_{t} | a_{(t-1) : 1} ; \\theta_{c}\\right) R_{k}这里$m$是控制器在训练过程中一个batch生成的子网络数量,$T$是生成的每个子网络的超参数数量,$R_{k}$是第k个子网络在验证集上的准确率。上述的估计是无偏的,还会有很高的方差,为了降低方差,作者使用了baseline function: \\frac{1}{m} \\sum_{k=1}^{m} \\sum_{t=1}^{T} \\nabla_{\\theta_{c}} \\log P\\left(a_{t} | a_{(t-1) : 1 ; \\theta_{c}}\\right)\\left(R_{k}-b\\right)这里$b$是之前子网络准确率的指数移动平均值。 4、增加网络复杂度:跨连接和其他类型的层现代网基本都会有跨连接层,比如残差网络和谷歌网络。为了能生成类似这样的网络结构,作者用了基于注意力机制的set-selection。主要做法就是,在RNN的预测上多加一个锚点(anchor point)。比如,在第N层的预测中,锚点会指示连接前N-1层中的哪几个。相当于有N-1个sigmoid函数,每个sigmoid是当前RNN隐藏状态和前N-1个锚点隐藏状态的函数: \\mathrm{P}(\\text { Layer } \\mathrm{j} \\text { is an input to layer i })=\\operatorname{sigmoid}\\left(v^{\\mathrm{T}} \\tanh \\left(W_{\\text {prev}} * h_{j}+W_{\\text {curr}} * h_{i}\\right)\\right)$h_{j}$是RNN在第$j$层锚点的隐藏状态,$j$从0到N-1。然后从这些sigmoid中参与决定当前层和前几层中的哪些连接。矩阵$W_{prev}, W_{curr}, v$是可训练的参数。 上面的做法可能会导致“compilation failures”。第一,如果一个层有很多个输入,那么这些输入会在特征图的深度这个维度上被连接,这可能导致各个输入特征图的尺寸不兼容。第二,有些层可能没有输入或输出。作者使用了三个技巧:(1)如果一个层没有输入,那么最一开始的图片作为输入;(2)在最后一层,我们将所有没有被连接的输出都连接起来,然后送入分类器;(3)如果输入特征图的尺寸不一致,,那就填充他们使其有相同的尺寸。 5、生成循环神经网络的cell作者在这部分给出了生成训练神经网络的方法。对的,NAS不仅可以生成CNN,还可以生成RNN。在每个时间步t,控制器需要找到$h_{t}$关于$h_{t-1}, x_{t}$的函数类型。例如最基本的RNN的cell是$h_{t}=\\tanh \\left(W_{1} \\ast x_{t}+W_{2} \\ast h_{t-1}\\right)$ RNN和LSTM的cell可以看做以$h_{t-1}, x_{t}$为输入去生成$h_{t}$的一系列步骤的树状结构。控制器需要标记树种每个节点的连接方式(加、对应元素相乘等)和激活函数(tanh,sigmoid等),然后融合两个输入作为输出。然后两个输出再作为下个节点的输入。对于LSTM,还需要处理记忆状态$c_{t}$和$c_{t-1}$,我们需要控制器预测树中的哪两个节点和这两个记忆状态相连。以两个节点的树为例,下图展示了其搜索过程:左节点标号为0,右节点标号为1,内节点标号为2。控制器的前三个block预测了每个节点的连接方式和激活函数,后两个block预测了$c_{t}$和$c_{t-1}$如何和树中节点相连。整个计算步骤如下: 对于节点0,控制器预测了Add和Tanh,即:$a_{0}={\\rm tanh} (W_{1} \\ast x_{t}+W_{2} \\ast h_{t-1})$ 对于节点1,控制器预测了ElemMult和ReLu,即:$a_{1}={\\rm ReLU}((W_{3} \\ast x_{t}) \\odot (W_{4} \\ast h_{t-1}))$ 在cell index这个block里,第二个元素预测了0,表示$c_{t-1}$会连接节点0;cell inject这个block里,预测了Add和ReLu,即节点0的输出会和$c_{t-1}$重新连接:$a_{0}^{new}={\\rm ReLu}(a_{0}+c_{t-1})$ 对于节点2,控制器预测了ElemMult和sigmoid,即$h_{t}=a{2}={\\rm sigmoid}(a_{0}^{new}\\bigodot a_{1})$ 在cell index这个block里,第一个元素预测了1,表示将节点1未经激活函数的输出作为$c_{t}$,即:$c_{t}=(W_{3} \\ast x_{t}) \\odot(W_{4} \\ast h_{t-1})$ 上述例子中,树有两个叶子节点,作者在实际的实验中,使用了8个叶子节点。 以上就是此篇论文最核心的方法,有关更多的实验细节和结果可以参看原文。此方法使用强化学习去搜索网络结构,由于每生成一种结构都要训练一遍并评估,而且强化学习需要采样很多很多次,整个搜索会非常耗时。在CIRAR-10数据及上搜索网络,作者用了800块GPU同时训练,可见其有多耗时耗力。NAS的方法如果都这么耗时的话是非常不划算的,如何快速有效地搜索是AutoDL努力的方向之一。 三、Designing Neural Network Architectures Using Reinforcement Learning在传统的强化学习设置中,过度的探索(exploration)可能会导致收敛变慢,而过度的利用(exploitation)会导致收敛到局部最优。最这篇论文中,作者组合了两种解决方法:带$\\epsilon$贪婪决策和经验回收(experience replay)的Q-learning。 1、Q-learning相关假定在一个有限状态的环境里,我们教智能体以马尔科夫决策过程(Markov Decision Process, MDP)寻找最优路径。环境是有限状态的这样保证智能体可以在有限步里确切地终止搜索。我们同时限制环境的状态空间$\\mathcal S$和动作空间$\\mathcal U$是离散的和有限的。对于任一状态$s_{i} \\in \\mathcal S$,都有有限的动作集合$\\mathcal U(s_{i}) \\subseteq \\mathcal U$可以选择。智能体以$p_{s^{\\prime}|s,u}(s_{j}|s_{i},u)$的概率,从状态$s_{i}$采取某一动作$u \\in \\mathcal U(s_{i})$转移到状态$s_{j}$。在每一个时间步t,智能体将得到奖励$r_{t}$。智能体的目标是在所有可能的轨迹上最大化总体期望奖励:${\\rm max}_{\\mathcal T_{i} \\in \\mathcal T}R_{\\mathcal T_{i}}$。对于一条轨迹它的总体期望奖励为: R_{\\mathcal{T}_{i}}=\\sum_{\\left(s, u, s^{\\prime}\\right) \\in \\mathcal{T}_{i}} \\mathbb{E}_{r | s, u, s^{\\prime}}\\left[r | s, u, s^{\\prime}\\right]对于任一状态$s_{i} \\in \\mathcal S$和动作$u \\in \\mathcal U(s_{i})$,我们定义最大化的期望奖励为$Q^{\\ast}(s_{i}, u)$。$Q^{\\ast}(\\cdot)$称为动作值函数,$Q^{\\ast}(s_{i}, u)$的值称为Q-值。用贝尔曼方差递归地最大化这个值: Q^{\\ast}\\left(s_{i}, u\\right)=\\mathbb{E}_{s_{j} | s_{i}, u}\\left[\\mathbb{E}_{r | s_{i}, u, s_{j}}\\left[r | s_{i}, u, s_{j}\\right]+\\gamma \\max _{u^{\\prime} \\in \\mathcal{U}\\left(s_{j}\\right)} Q^{\\ast}\\left(s_{j}, u^{\\prime}\\right)\\right]在很多情况下,得到这个方差的解析解是不可能的,我们可以迭代地求解: Q_{t+1}\\left(s_{i}, u\\right)=(1-\\alpha) Q_{t}\\left(s_{i}, u\\right)+\\alpha\\left[r_{t}+\\gamma \\max _{u^{\\prime} \\in \\mathcal{U}\\left(s_{j}\\right)} Q_{t}\\left(s_{j}, u^{\\prime}\\right)\\right]此方差包括两个参数:(1)$\\alpha$是Q-learning rate,决定了新信息相对于旧信息的权重;(2)$\\gamma$是折扣因子(discount factor),决定了短期奖励相对于长期奖励的权重。Q-learning是免模型的算法,即不需要明确地构建环境的估计。此外Q-learning是off policy的,这意味着它可以通过非最优行为分布来探索最优策略。 我们通过$\\epsilon$贪婪策略选择动作,以$\\epsilon$的概率选择随机动作,以$1-\\epsilon$的概率选择贪婪动作${\\rm max}_{u \\in {\\mathcal u(s_{i})}}Q(s_{i}, u)$。我们慢慢地将$\\epsilon$从1降为0,智能体从探索阶段慢慢变为利用阶段。当探索的代价比较大时,可以使用经验回放来加速收敛。在经验回放里,之前被探索过的路径和奖励被存储下来,智能体在探索的过程中可以利用之前的信息更新Q-值。 2、搜索方法总览下图展示了一个例子:上图中左边显示了可行状态和动作空间,以及agent可能采取的潜在轨迹;右边展示了该轨迹定义的CNN架构。智能体循序地选择神经网络层。选择层的过程是马尔科夫决策过程,假设在一个网络中表现最好的层在另一个网络中应该也是表现最好的。智能体通过$\\epsilon$贪婪策略选择层,直到终止条件。训练在此路径下构建的网络,得到验证集上的准确率。验证集准确率和结构描述被存储起来,用于经验回放。智能体遵循着$\\epsilon$变化表,从探索阶段过渡到利用阶段。 3、状态空间每个状态被定义为相关层的所有参数组成的元组,。这里采用五种不同的层:卷积层(C),池化层(P),全连接层(FC),全局平均池化(GAP),和softmax(SM)。下图展示了相关层的参数: 其中每个层有个参数:layer depth层深度。向状态空间添加层深度允许我们压缩操作空间,以便状态操作图是有向无环图(DAG),还允许我们指定智能体在终止之前可以选择的最大层数。每个层还有另一个参数:representations size(R-size)。通过池化和卷积,原始图像被逐渐地压缩尺寸,这可能会导致在采样过程中,特征图被压缩得太小以至于无法继续往下进行。例如五个$2 \\times 2$大小步长为2的池化会将$32 \\times 32$的图像压缩至$1 \\times 1$,使接下来的操作无法进行。使用R-size可以限制一些操作,这些操作使大小为n的特征图变为小于等于n的特征图。在实际构建状态空间时,R-size被分箱成3个桶:$\\{(\\infty, 8], (8, 4], (4, 1] \\}$。这样的分箱操作会增加状态转移的不确定性:根据当前特征图的大小,池化或卷积可能会改变R-size,也可能不会。例如下图: 假设有两个R-size bins, R-size Bin1: $[8, \\infty)$和R-size Bin2:$(0, 7]$,图a给出了初始状态为R-size Bin1,真实的表示大小为18的情况。当代理选择使stride为2大小为2×2的池化后,真实的表示大小减小到9,但是R-size bin没有变化。在图b中,stride为2大小为2×2的池层将实际表示大小从14减小到7,但是bin更改为R-size Bin2。因此,在图a和b中,智能体最终处于不同的最终状态,尽管初始状态相同,并且选择了相同的操作。图c显示,在我们的状态-操作空间中,当智能体采取降低表示大小的操作时,它将不确定将转换到哪个状态。 4、动作空间首先,我们允许智能体在路径上的任一点终止。此外我们只允许从layer depth为$i$的状态转移到layer depth为$i-1$的状态,这样保证是有向无环图。任何达到最大layer depth的层都会转移到终止状态。接下来,作者将FC层的数量限制为最多两个,因为大量的FC层可能导致太多可学习的参数。当且仅当连续FC状态的数量小于允许的最大值时,位于FC状态的才可以转换到另一个FC类型状态。并且,有$d$个节点的FC只能转移到$d^{\\prime}<d$的FC。然后是一些其他的限制。卷积层可以转移到其他任何类型的层,池化层可以转移到除池化层以外的其他任何层,因为连续的池化相当于一个不在可选状态空间里的大尺寸池化层。只有R-size为(8, 4]和(4, 1]的状态才能转移到FC层,这样保证权重参数不会特别巨大。 5、Q-learning训练过程在训练部分,作者设置Q-learning rate($\\alpha$)为0.01,折扣因子$\\gamma$为1。$\\epsilon$以一定的步长从1.0逐渐降为0.1,步长由训练的唯一模型的数量决定,如下表所示:每一条被智能体采样的轨迹,即一个网络结构,被训练,然后得到验证集的准确率,这个网络结构描述和准确率被记录下来。从上表可以看出,在$\\epsilon =1$的时候相对于其他值训练了大量的模型,这是为了保证智能体在“利用”之前有充足的机会去“探索”。智能体在$\\epsilon =0.1$的时候终止搜索,以获得随机的最终决策。因为$\\epsilon =0$表示确定的决策,而作者希望能获得几个比较好的模型去做集成,所以使$\\epsilon =0.1$以保证最终的决策是随机的。对于整个训练过程,一个“回放字典”(replay dictionary)被维护,这里存储被采样过的网络及验证集在上面的准确率。如果采样到的模型已经在这个字典里,那这个模型不用背重新训练,其验证集准确率会直接被拿来使用。当每个模型被采样后和训练后,智能体从回放字典里随机采样100个模型,并对每个采样轨迹中的所有转移序列应用到公式3中,转移序列在时间上被倒转,这样做可以加快Q-值的收敛。 6、伪代码算法1描述了训练的主循环,参数$M$决定了在给定一个$\\epsilon$的情况下训练多少个模型,参数$K$表示在每一轮迭代中,从回放字典中采样多少次取更新Q-值。TRAIN 函数表示训练一个确定的模型并返回验证集准确率。 算法2描述了使用$\\epsilon$贪婪策略采样新网络的细节。TRANSITION函数在给定一个状态和动作的情况下返回下一步的状态。 算法3描述了公式3的实现细节,折扣因子设为1,对整个状态序列进行时间倒转。 上述所有就是此论文的核心方法,此方法也是非常耗时,对于每个数据集,作者使用10块GPU训练了8-10天。(这篇论文在写作上感觉要比上篇的好,细节清楚) 参考文献 https://www.automl.org/best-practices-for-scientific-research-on-neural-architecture-search/ Zoph B , Le Q V . Neural Architecture Search with Reinforcement Learning[J]. 2016. Designing Neural Network Architectures using Reinforcement Learning https://blog.csdn.net/u014380165/article/details/78525500 https://blog.csdn.net/xjz18298268521/article/details/79078835/ 《深入理解AutoML和AutoDL》","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"http://yoursite.com/tags/深度学习/"},{"name":"自动化","slug":"自动化","permalink":"http://yoursite.com/tags/自动化/"},{"name":"NAS","slug":"NAS","permalink":"http://yoursite.com/tags/NAS/"},{"name":"AutoDL","slug":"AutoDL","permalink":"http://yoursite.com/tags/AutoDL/"}]},{"title":"机器学习的数学基础:概率论","slug":"机器学习的数学基础:概率论","date":"2019-09-23T15:48:07.000Z","updated":"2019-09-28T09:39:36.000Z","comments":true,"path":"2019/09/23/机器学习的数学基础:概率论/","link":"","permalink":"http://yoursite.com/2019/09/23/机器学习的数学基础:概率论/","excerpt":"","text":"一、概率论公理1、样本空间和事件 对于一个试验,所有可能的结果构成的集合,称为该试验的样本空间,并即为$S$。 并:对于一个样本空间$S$的任意两个事件$E$和$F$,事件$E\\bigcup F$称为$E$和$F$的并。 交:$EF$或$E\\bigcap F$,即事件$E$和$F$同时发生 若$EF=\\emptyset$,称事件$E$和$F$互不相容 补:$\\overline E$,包含在样本空间但不包含在$E$中的所有结果 事件的交、并、补遵循的运算法则: 交换律:$E \\bigcup F=F \\bigcup E$, $EF=FE$ 结合律:$(E \\bigcup F) \\bigcup G=E \\bigcup (F \\bigcup G)$, $(EF)G=E(FG)$ 分配率:$(E \\bigcup F) G=EG \\bigcup FG$, $EF \\bigcup G=(E \\bigcup G)(F \\bigcup G)$ 摩根定律: \\overline{\\left(\\bigcup_{i=1}^{n}E_{i} \\right)} = \\bigcap_{i=1}^{n}\\overline{E_{i}} \\\\ \\overline{\\left(\\bigcap_{i=1}^{n}E_{i} \\right)} = \\bigcup_{i=1}^{n}\\overline{E_{i}}2、概率论公理 定义事件E的概率$P(E)$为E发生的次数占试验总次数的比例的极限: P(E)=\\lim_{n \\to \\infty} \\frac{n(E)}{n}概率论的三个公理: 公理1: 0 \\leq P(E) \\leq 1 公理2: P(S)=1 公理3:对任一系列互不相容事件$E_{1}, E_{2}, …$,有: P\\left(\\bigcup_{i=1}^{\\infty}E_{i} \\right)= \\sum_{i=1}^{\\infty}P(E_{i}) 我们把满足以上3条公理的$P(E)$称为事件E的概率 3、几个简单的命题 命题1: P(\\overline E)=1-P(E) 命题2: P(E \\bigcup F)=P(E)+P(F)-P(EF) 命题3: P\\left(\\bigcup_{i=1}^{n}E_{i} \\right)= \\sum_{r=1}^{n}(-1)^{r+1} \\sum_{i_{1}","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"机器学习","slug":"机器学习","permalink":"http://yoursite.com/tags/机器学习/"},{"name":"数学","slug":"数学","permalink":"http://yoursite.com/tags/数学/"},{"name":"概率论","slug":"概率论","permalink":"http://yoursite.com/tags/概率论/"}]},{"title":"机器学习——逻辑回归","slug":"机器学习——逻辑回归","date":"2019-08-28T10:32:44.000Z","updated":"2019-08-28T12:43:52.000Z","comments":true,"path":"2019/08/28/机器学习——逻辑回归/","link":"","permalink":"http://yoursite.com/2019/08/28/机器学习——逻辑回归/","excerpt":"","text":"一、逻辑回归1、逻辑斯谛回归模型我们可以用线性模型$z=\\boldsymbol{w}^{\\mathrm{T}} \\boldsymbol{x}+b$ 来做回归任务,如果我们用此线性模型来做分类任务,需要用一个单调可微函数$g(\\cdot)$将分类任务的真实标签$y$与线性回归模型的预测值联系起来: y=g^{-1}\\left(\\boldsymbol{w}^{\\mathrm{T}} \\boldsymbol{x}+b\\right)\\tag{1}这里我们使用对数几率函数(logistic function): y=\\frac{1}{1+e^{-z}} \\tag{2}它将$z$值转化为一个接近0或1的值,结合(1)和(2)可以得到: y=\\frac{1}{1+e^{-\\left(\\boldsymbol{w}^{\\mathrm{T}} \\boldsymbol{x}+b\\right)}} \\tag{3} \\ln \\frac{y}{1-y}=\\boldsymbol{w}^{\\mathrm{T}} \\boldsymbol{x}+b将$y$视为样本$\\boldsymbol{x}$是正例的可能性,则$1-y$是其反例的可能性,其比值$\\frac y{1-y}$称为几率(odds),反映了$\\boldsymbol{x}$作为正例的相对可能性,取对数得到$\\mathrm{ln}\\frac y{1-y}$,称为对数几率。这实际上使用线性回归模型的预测结果取逼近真实标签的对数几率。 上面提到$y$视为样本$\\boldsymbol{x}$是正例的可能性,即$y=p(y=1|\\boldsymbol{x})$,显然有: \\begin{aligned} p(y&=1 | \\boldsymbol{x})=\\frac{e^{\\boldsymbol{w}^{\\mathrm{T}} \\boldsymbol{x}+b}}{1+e^{\\boldsymbol{w}^{\\mathrm{T}} \\boldsymbol{x}+b}} \\\\ p(y&=0 | \\boldsymbol{x})=\\frac{1}{1+e^{\\boldsymbol{w}^{\\mathrm{T}} \\boldsymbol{x}+b}} \\end{aligned}2、极大似然估计我们的任务就是估计参数$\\boldsymbol{w}$和$b$,可以使用极大似然估计。给定数据集$\\left\\{\\left(\\boldsymbol{x}_{i}, y_{i}\\right)\\right\\}_{i=1}^{m}$,并设$\\boldsymbol{\\theta}=\\left\\{\\boldsymbol{w}, b\\right\\}$,$h_{\\theta}(\\boldsymbol{x})=\\frac{e^{\\boldsymbol{w}^{\\mathrm{T}} \\boldsymbol{x}+b}}{1+e^{\\boldsymbol{w}^{\\mathrm{T}} \\boldsymbol{x}+b}}$,那么: \\begin{array}{l}{p(y=1 | \\boldsymbol{x} ; \\boldsymbol{\\theta})=h_{\\theta}(\\boldsymbol{x})} \\\\ {p(y=0 | \\boldsymbol{x} ; \\boldsymbol{\\theta})=1-h_{\\theta}(\\boldsymbol{x})}\\end{array}上式可以合写为: p(y | \\boldsymbol{x} ; \\boldsymbol{\\theta})=\\left(h_{\\theta}(\\boldsymbol{x})\\right)^{y}\\left(1-h_{\\theta}(\\boldsymbol{x})\\right)^{1-y} \\tag{4}则可以写出似然函数: \\begin{aligned} L(\\boldsymbol{\\theta}) &=p(Y | \\boldsymbol{X} ; \\boldsymbol{\\theta}) \\\\ &=\\prod_{i=1}^{m} p\\left(y_{i} | \\boldsymbol{x}_{i} ; \\boldsymbol{\\theta}\\right) \\\\ &=\\prod_{i=1}^{m}\\left(h_{\\theta}\\left(\\boldsymbol{x}_{i}\\right)\\right)^{y_{i}}\\left(1-h_{\\theta}\\left(\\boldsymbol{x}_{i}\\right)\\right)^{1-y_{i}} \\end{aligned}取对数得到对数似然函数: \\begin{aligned} \\ell(\\boldsymbol{\\theta}) &=\\log L(\\boldsymbol{\\theta}) \\\\ &=\\sum_{i=1}^{m} y_{i} \\log h_{\\theta}\\left(\\boldsymbol{x}_{i}\\right)+\\left(1-y_{i}\\right) \\log \\left(1-h_{\\theta}\\left(\\boldsymbol{x}_{i}\\right)\\right) \\end{aligned}每个样本属于真实标签的概率越大越好,因此要最大化对数似然函数: \\begin{equation} {\\theta}^{*}=\\underset{\\theta}{\\arg \\max } \\ell(\\theta) \\end{equation}$\\ell(\\boldsymbol{\\theta})$是关于$\\boldsymbol{\\theta}$的高阶连续可导凸函数,可以使用梯度下降法、牛顿法等得到其最优解。以梯度下降法为例,我们需要最小化$-\\ell(\\boldsymbol{\\theta})$: \\begin{equation} {\\boldsymbol{\\theta}}^{*}=\\underset{\\theta}{\\arg \\min } -\\ell(\\boldsymbol{\\theta}) \\end{equation}对其求导得: \\begin{aligned} \\frac{\\partial}{\\partial \\boldsymbol{\\theta}} \\ell(\\boldsymbol{\\theta}) &=\\left(-y \\frac{1}{h_{\\theta}\\left(\\boldsymbol{x}\\right)}+(1-y) \\frac{1}{1-h_{\\theta}\\left(\\boldsymbol{x} \\right)}\\right) \\frac{\\partial}{\\partial \\boldsymbol{\\theta}} h_{\\theta}\\left(\\boldsymbol{x}\\right) \\\\ &=\\left(-y \\frac{1}{h_{\\theta}\\left(\\boldsymbol{x} \\right)}+(1-y) \\frac{1}{1-h_{\\theta}\\left(\\boldsymbol{x} \\right)}\\right) h_{\\theta}\\left(\\boldsymbol{x} \\right) \\left(1-h_{\\theta}(\\boldsymbol{x} ) \\right) \\frac{\\partial}{\\partial \\boldsymbol{\\theta}} \\boldsymbol{\\theta}^{\\mathrm T} \\boldsymbol{x} \\\\ &=\\left(-y\\left(1-h_{\\theta}\\left(\\boldsymbol{x} \\right)\\right)+(1-y) h_{\\theta}\\left(\\boldsymbol{x}\\right)\\right) \\boldsymbol{x} \\\\ &=\\left(h_{\\theta}(\\boldsymbol{x})-y\\right) \\boldsymbol{x} \\end{aligned}再更新梯度: \\boldsymbol{\\theta} :=\\boldsymbol{\\theta}-\\alpha\\left(h_{\\theta}\\left(\\boldsymbol{x}\\right)-y\\right) \\boldsymbol{x}参考资料 周志华《机器学习》 李航《统计学习方法》 吴恩达《机器学习》公开课","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"机器学习","slug":"机器学习","permalink":"http://yoursite.com/tags/机器学习/"},{"name":"逻辑回归","slug":"逻辑回归","permalink":"http://yoursite.com/tags/逻辑回归/"}]},{"title":"机器学习——朴素贝叶斯","slug":"机器学习——朴素贝叶斯","date":"2019-08-28T02:45:05.000Z","updated":"2019-08-28T12:43:48.000Z","comments":true,"path":"2019/08/28/机器学习——朴素贝叶斯/","link":"","permalink":"http://yoursite.com/2019/08/28/机器学习——朴素贝叶斯/","excerpt":"","text":"一、贝叶斯决策论设输入空间$\\mathcal{X} \\in \\Bbb R^{d}$是$d$维向量的集合,输出空间为标签集合$\\mathcal{Y}=\\left\\{c_{1}, c_{2}, \\dots, c_{N} \\right\\}$,$X$是定义在输入空间上的随机变量,$Y$是定义在输出空间上的随机变量,$P(X,Y)$是$X$和$Y$联合概率分布,训练集$D=\\left\\{(\\boldsymbol{x}_{1}, y_{1}), (\\boldsymbol{x}_{2}, y_{2}), \\dots, (\\boldsymbol{x}_{m}, y_{m})\\right\\}$,由$P(X,Y)$独立同分布地产生。 对于标签集合$\\mathcal{y}=\\left\\{c_{1}, c_{2}, \\dots, c_{N} \\right\\}$,$\\lambda_{ij}$是将一个真实标记为$c_{j}$的样本误分类为$c_{i}$所产生的损失。将$\\boldsymbol{x}$分类为$c_{i}$所产生的期望损失即条件风险为: R\\left(c_{i} | \\boldsymbol{x}\\right)=\\sum_{j=1}^{N} \\lambda_{i j} P\\left(c_{j} | \\boldsymbol{x}\\right)我们的目标是最小化总体风险: R(h)=\\mathbb{E}_{\\boldsymbol{x}}[R(h(\\boldsymbol{x}) | \\boldsymbol{x})]$h$为判定准则:$h : \\mathcal{X} \\mapsto \\mathcal{Y}$。 贝叶斯判定准则:为最小化总体风险,只需在每个样本上选择哪个能使条件风险$R(c|\\boldsymbol{x})$最小的标签,即: h^{*}(\\boldsymbol{x})=\\underset{c \\in \\mathcal{Y}}{\\arg \\min } R(c | \\boldsymbol{x})$h^{\\ast}$是贝叶斯最优分类器,$R(h^{\\ast})$是贝叶斯风险。 对于误判损失$\\lambda_{ij}$可以采用0-1损失: \\lambda_{i j}=\\left\\{\\begin{array}{ll}{0,} & {\\text { if } i=j} \\\\ {1,} & {\\text { otherwise }}\\end{array}\\right.此时条件风险可写为:$R(c | \\boldsymbol{x})=1-P(c | \\boldsymbol{x})$,最优贝叶斯分类器为: h^{*}(\\boldsymbol{x})=\\underset{c \\in \\mathcal{Y}}{\\arg \\max } P(c | \\boldsymbol{x})要得到最优分类器,首先要得到后验概率$P(c | \\boldsymbol{x})$: P(c | \\boldsymbol{x})=\\frac{P(\\boldsymbol{x}, c)}{P(\\boldsymbol{x})}根据贝叶斯定理,上式可写为: P(c | \\boldsymbol{x})=\\frac{P(c) P(\\boldsymbol{x} | c)}{P(\\boldsymbol{x})}$P(c)$是先验概率,$P(\\boldsymbol{x}|c)$是条件概率或似然,$P(\\boldsymbol{x})$与标签无关,因此贝叶斯法是学习$P(c)$及$P(\\boldsymbol{x}|c)$。 对于$P(c)$,根据大数定律,当训练集包含充足的独立同分布样本时,$P(c)$可以通过样本出现的频率来估计。 对于$P(\\boldsymbol{x}|c)$,有指数级的参数,样本取值可能在训练集中未出现,直接使用频率估计不可行。 二、朴素贝叶斯分类器朴素贝叶斯采用属性条件独立假设,对已知类别,假设所有属性相互独立,即: P(\\boldsymbol{x}|c)=P(x_{1}, x_{2}, \\dots, x_{d}|c)=\\prod_{j=1}^dP(x_{i}|c)则$P(c | \\boldsymbol{x})$可写为: P(c | \\boldsymbol{x})=\\frac{P(c) P(\\boldsymbol{x} | c)}{P(\\boldsymbol{x})}=\\frac{P(c)}{P(\\boldsymbol{x})} \\prod_{i=1}^{d} P\\left(x_{i} | c\\right)那么贝叶斯判定准则为: h_{n b}(\\boldsymbol{x})=\\underset{c \\in \\mathcal{Y}}{\\arg \\max } P(c) \\prod_{i=1}^{d} P\\left(x_{i} | c\\right)令$D_{c}$表示训练集$D$中第$c$类样本的集合,那么容易得先验概率为: P(c)=\\frac{\\left|D_{c}\\right|}{|D|}再令$D_{c,x_{i}}$表示$D_{c}$中在第$i$个属性上取值为$x_{i}$的样本集合,那么条件概率可估计为: P\\left(x_{i} | c\\right)=\\frac{\\left|D_{c, x_{i}}\\right|}{\\left|D_{c}\\right|}当某个属性在训练集中没有与某个类别同时出现时,在连乘时会出现0,此时可以用拉普拉斯修正: \\begin{aligned} \\hat{P}(c) &=\\frac{\\left|D_{c}\\right|+1}{|D|+N} \\\\ \\hat{P}\\left(x_{i} | c\\right) &=\\frac{\\left|D_{c, x_{i}}\\right|+1}{\\left|D_{c}\\right|+N_{i}} \\end{aligned}$N$为类别数,$N_{i}$为第$i$个属性可能的取值数目。 参考资料 周志华《机器学习》 李航《统计学习方法》","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"github","slug":"github","permalink":"http://yoursite.com/tags/github/"},{"name":"机器学习","slug":"机器学习","permalink":"http://yoursite.com/tags/机器学习/"},{"name":"贝叶斯","slug":"贝叶斯","permalink":"http://yoursite.com/tags/贝叶斯/"}]},{"title":"生成星辰大海——变分自编码器(VAE)实践","slug":"生成星辰大海——变分自编码器(VAE)实践","date":"2019-08-16T10:26:02.000Z","updated":"2020-08-03T02:57:18.000Z","comments":true,"path":"2019/08/16/生成星辰大海——变分自编码器(VAE)实践/","link":"","permalink":"http://yoursite.com/2019/08/16/生成星辰大海——变分自编码器(VAE)实践/","excerpt":"","text":"相信大家对浩瀚的宇宙、漫天繁星都充满过极大的兴趣,我们对夜晚漫天眨眼的星星充满了无限的向往,在某个夏日夜晚里,我们也是那个“数星星的孩子”。在本文中,我们将使用变分自编码器(VAE)这项深度学习技术,生成星系的图片,生成我们的“星辰大海”。 一、变分自编码器自编码器(AutoEncoder)是一种表示学习技术,是深度学习中的一个重要分支,也是无监督学习的重要技术之一。一个典型的自编码器结构如Figure1所示,包括两个主要部分:编码器(Encoder)和解码器(Decoder)。编码器将输入图像不断压缩,最后形成一个特征表示向量,而解码器用这个表示向量将原图片重构出来。学习过程是最小化输出与输入之间的差异,这个差异用一个损失函数表示,比如均方误差。自编码器起到了降维的作用,用一个低维的向量空间来表示原始的高维空间。 而变分自编码器是怎么回事呢?先看一下典型的变分自编码的结构,如图2所示。 看上去很复杂,在编码器和解码器之间已经不是那个简单的特征表示向量了。作为生成式模型,VAE和生成式对抗网络GAN比较相似:都是希望通过一个隐变量Z生成数据x,使生成数据的分布$p(x|z)$接近于真实分布$p(x)$。假设$z$服从某种分布,比如正态分布。GAN直接由Z生成数据,和真实数据进行对抗训练。而VAE直接从输入数据中学习这种分布。我们现在假设Z服从标准正态分布,它有两个参数,均值${\\boldsymbol \\mu}$和方差${\\boldsymbol \\sigma}^2$。从图2中可看出,编码器就是来干这个事情的,它会拟合出均值${\\boldsymbol \\mu}$和方差的对数$\\log{\\boldsymbol \\sigma}^2$。这里不直接拟合${\\boldsymbol \\sigma}^2$是因为${\\boldsymbol \\sigma}^2$是非负的,需要加激活函数,而$\\log{\\boldsymbol \\sigma}^2$就可正可负了,不需要加激活函数。 由于Z要从$\\mathcal N(\\boldsymbol \\mu, {\\boldsymbol \\sigma}^2)$中采样,但这个采样的过程是不可导的,无法使用反向传播算法。对于这个问题,这里采用了reparameterization的技巧,即从$\\mathcal N(\\boldsymbol 0, \\boldsymbol I)$中采样一个$\\boldsymbol\\epsilon$,然后令$\\boldsymbol Z={\\boldsymbol \\sigma}^2 \\times {\\boldsymbol \\epsilon} + \\boldsymbol\\mu$,这样Z依然服从$\\mathcal N(\\boldsymbol \\mu, {\\boldsymbol \\sigma}^2)$,而这个过程是线性的,可以正常使用反向传播算法。 现在Z服从正态分布$\\mathcal N(\\boldsymbol \\mu, {\\boldsymbol \\sigma}^2)$,为了使$Z$服从标准正态分布$\\mathcal N(\\boldsymbol 0, \\boldsymbol I)$,我们将$\\mathcal N(\\boldsymbol \\mu, {\\boldsymbol \\sigma}^2)$与$\\mathcal N(\\boldsymbol 0, \\boldsymbol I)$的KL散度作为损失函数的一项,即正则化项,使$Z$趋向于标准正态分布。损失函数的另一项就是重构损失了,由于要使生成数据的分布$p(x|z)$接近于真实分布$p(x)$,所以这里使用两者的交叉熵损失作为重构损失。最后整个损失函数为: 那么说了这么多,VAE和普通的自编码器相比有什么优点呢?普通的自编码器更关注对原始数据的重构,生成的特征表示也是不连续的,那么如果从一片未学到的隐空间进行采样再进过解码器后,生成的数据可能是不真实的。而VAE学习到的是数据的分布,隐空间是连续的,可以从这个空间随意采样。下图是两个用VAE生成图片的例子。左边的图片是生成人脸,第一行从左往右看,人脸从朝右慢慢变成了朝左,往下看,人脸从面无表情慢慢变成了微笑。右边是生成的手写数字,从第一行向右看,数字从6慢慢变成了0,往下看,数字6慢慢变成了9,整个生成的图片都是渐变的。所以用VAE生成的图片可以有渐变的特性。 二、使用星系数据集实践VAE这里我们想要生成星系的图片,但没有这样的公开数据集,所以我自己从网上爬取了一些星系的图片,经过筛选与处理,最后得到了600多张图片。600多张说实话确实太少了,但质量比较高的关于星系的图片比较少,网上爬取的图片很多也都是重复的。这里我们用这个星系数据集为例,能使模型work起来,结果看得过去就算成功。 我收集到的星系数据集的一些样例如下图所示: 星系本身是非常漂亮的~ 下面是模型设计,这里我们使用全卷积网络(FCN)作为编码器,解码器结构与之对称。全卷积网络没有池化层,池化层使用带步长的卷积代替,这样可以让网络自己学习到合适的下采样策略。编码器encoder与解码器decoder的结构如表1和表2所示。编码器中有4层卷积,滤波器通道数目逐渐增加,已学到更丰富的特征。然后最后一层卷积的输出被展开成一维向量,分成两个支路,一个支路表示均值向量${\\boldsymbol \\mu}$,另一个支路表示$\\log{\\boldsymbol \\sigma}^2$。这里设它们是4维的。对于解码器,基本上使编码器的对称。由于编码器中图像经过卷积后尺寸是慢慢变小的,在解码器中用上采用层将图片慢慢变大。而通道数目是逐渐减少的,将丰富的信息逐渐整合成RGB三通道的图像。最后一层的激活函数是tanh函数,值在-1和1之间,会将其线性变换至0到255之间的整数。整个网络的输入输出均为256 256 3的图像。 下面是代码部分,整个代码是基于keras实现的。 数据读取部分:这里使用keras的ImageDataGenerator,它将读取数据、生成mini-batch、预处理、数据增强等都结合在了一起,非常方便。 编码器部分: 潜在空间采样函数: 解码器部分: 损失函数,一般keras的损失函数的定义形式为loss(input, target),但VAE的损失函数不符合这种形式,所以我们写一个自定义层,使用其内置的add_loss方法来创建自己想要的损失。 训练部分则是从ImageDataGenerator里获得mini-batch,用train_on_batch方法训练。这里batch size为64,一共训练10000轮。学习率设置为0.001,每100轮迭代减小一次学习率,优化器为Adam 最后是实验结果,下图是训练结束后生成的图像: 从上图中可以看出VAE已经学习到了星系相关的形状,从左上角向右看,形状由棒形变为不规则形状,向下看变为团状,颜色也是渐变的。不过缺点也是很明显的:不够清晰。这里有两个原因吧:第一是VAE生成图片的清晰度不如GAN,依靠对抗训练GAN可以获得高清晰度的图片。第二是数据量不够,只有600多张图片,模型能学习到的信息很有限,增加数据量可以进一步提高效果。 下一步的研究与工作: 1、 关于VAE的原理这里只是作了简单介绍,下一步会对VAE作详细的理论介绍 2、 在这个模型的基础上进一步想办法提高效果,尝试VAE的其他变种 3、增加数据量。希望对本项目和星系数据集感兴趣的小伙伴们,能收集更多的数据,在本项目的github地址上提交,一起来提升图片生成的质量~ 本项目地址:https://github.com/5663015/galaxy_generation 参考资料:[1] Kingma, D. P., & Welling, M. (2013). Auto-encoding variational bayes. arXiv preprint arXiv:1312.6114. [2] 苏剑林. (2018, Mar 18). 《变分自编码器(一):原来是这么一回事 》[Blog post]. Retrieved from https://spaces.ac.cn/archives/5253 [3] https://www.cnblogs.com/fxjwind/p/9099931.html","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"http://yoursite.com/tags/深度学习/"},{"name":"自编码器","slug":"自编码器","permalink":"http://yoursite.com/tags/自编码器/"}]},{"title":"机器学习——线性模型","slug":"机器学习——线性模型","date":"2019-06-24T13:25:17.000Z","updated":"2019-08-27T03:11:26.000Z","comments":true,"path":"2019/06/24/机器学习——线性模型/","link":"","permalink":"http://yoursite.com/2019/06/24/机器学习——线性模型/","excerpt":"","text":"一、线性回归给定由$d$个属性描述的样本$\\boldsymbol{x}=(x_1; x_2; .. ; x_d)$(列向量),数据集$D=\\left\\{\\left(\\boldsymbol{x}_{1}, y_{1}\\right),\\left(\\boldsymbol{x}_{2}, y_{2}\\right), \\ldots,\\left(\\boldsymbol{x}_{m}, y_{m}\\right)\\right\\}$,线性回归试图学得一个线性组合来进行预测的函数: f(\\boldsymbol{x})=\\boldsymbol{w}^{\\mathrm{T}} \\boldsymbol{x}+b其中权重$\\boldsymbol{w}=\\left(w_{1} ; w_{2} ; \\ldots ; w_{d}\\right)$,偏置$b$为标量 1、一元线性回归先考虑最简单的情况:样本只有一个属性,即一元线性回归。此问题试图学得 f\\left(x_{i}\\right)=w x_{i}+b, 使得 f\\left(x_{i}\\right) \\simeq y_{i}这里使用均方误差来衡量$f(x)$与$y$之间的差别,使得均方误差最小的$w$和$b$即为所求,即: \\begin{aligned}\\left(w^{*}, b^{*}\\right) &=\\underset{(w, b)}{\\arg \\min } \\sum_{i=1}^{m}\\left(f\\left(x_{i}\\right)-y_{i}\\right)^{2} \\\\ &=\\underset{(w, b)}{\\arg \\min } \\sum_{i=1}^{m}\\left(y_{i}-w x_{i}-b\\right)^{2} \\end{aligned}基于均方误差最小化来求解模型的方法称为最小二乘法,就是试图找到一条直线,是所有样本到直线上的欧式距离之和最小。设损失$E=\\sum_{i=1}^{m}\\left(f\\left(x_{i}\\right)-y_{i}\\right)^{2}$,将$E$对$w$和$b$分别求导,得到: \\begin{aligned} \\frac{\\partial E_{(w, b)}}{\\partial w} &=2\\left(w \\sum_{i=1}^{m} x_{i}^{2}-\\sum_{i=1}^{m}\\left(y_{i}-b\\right) x_{i}\\right) \\\\ \\frac{\\partial E_{(w, b)}}{\\partial b} &=2\\left(m b-\\sum_{i=1}^{m}\\left(y_{i}-w x_{i}\\right)\\right) \\end{aligned}然后令两式都为0,得到解析解: w=\\frac{\\sum_{i=1}^{m} y_{i}\\left(x_{i}-\\overline{x}\\right)}{\\sum_{i=1}^{m} x_{i}^{2}-\\frac{1}{m}\\left(\\sum_{i=1}^{m} x_{i}\\right)^{2}} \\\\ b=\\frac{1}{m} \\sum_{i=1}^{m}\\left(y_{i}-w x_{i}\\right)其中$\\overline{x}=\\frac{1}{m} \\sum_{i=1}^{m} x_{i}$为均值。 2、多元线性回归而更一般的情况是本文开头所给出的数据集的形式,样本有$d$个属性,此时我们试图学得 f\\left(\\boldsymbol{x}_{i}\\right)=\\boldsymbol{w}^{\\mathrm{T}} \\boldsymbol{x}_{i}+b, 使得f\\left(x_{i}\\right) \\simeq y_{i}这里我们将权重$\\boldsymbol{w}$和偏置$b$整合在一起:$\\hat{\\boldsymbol{w}}=(\\boldsymbol{w} ; b)$,同样得数据集$D$表示为一个$m \\times (d+1)$大小的矩阵$\\boldsymbol{X}$,即: \\mathbf{X}=\\left(\\begin{array}{ccccc}{x_{11}} & {x_{12}} & {\\dots} & {x_{1 d}} & {1} \\\\ {x_{21}} & {x_{22}} & {\\dots} & {x_{2 d}} & {1} \\\\ {\\vdots} & {\\vdots} & {\\ddots} & {\\vdots} & {\\vdots} \\\\ {x_{m 1}} & {x_{m 2}} & {\\dots} & {x_{m d}} & {1}\\end{array}\\right)=\\left(\\begin{array}{cc}{\\boldsymbol{x}_{1}^{\\mathrm{T}}} & {1} \\\\ {\\boldsymbol{x}_{2}^{\\mathrm{T}}} & {1} \\\\ {\\vdots} & {\\vdots} \\\\ {\\boldsymbol{x}_{m}^{\\mathrm{T}}} & {1}\\end{array}\\right)标签也写成向量形式$\\boldsymbol{y}=\\left(y_{1} ; y_{2} ; \\ldots ; y_{m}\\right)$,我们同样使用均方误差最小化: \\hat{\\boldsymbol{w}}^{*}=\\underset{\\boldsymbol{\\hat { w }}}{\\arg \\min }(\\boldsymbol{y}-\\mathbf{X} \\hat{\\boldsymbol{w}})^{\\mathrm{T}}(\\boldsymbol{y}-\\mathbf{X} \\hat{\\boldsymbol{w}})令$E=(\\boldsymbol{y}-\\mathbf{X} \\hat{\\boldsymbol{w}})^{\\mathrm{T}}(\\boldsymbol{y}-\\mathbf{X} \\hat{\\boldsymbol{w}})$,展开得到: E=\\boldsymbol{y}^{T} \\boldsymbol{y}-\\boldsymbol{y}^{T} \\mathbf{X} \\hat{\\boldsymbol{w}}-\\hat{\\boldsymbol{w}}^{T} \\mathbf{X}^{T} \\boldsymbol{y}+\\hat{\\boldsymbol{w}}^{T} \\mathbf{X}^{T} \\mathbf{X} \\hat{\\boldsymbol{w}}对$\\hat{\\boldsymbol{w}}$求导得到: \\begin{array}{c}{\\frac{\\partial E}{\\partial \\hat{\\boldsymbol{w}}}=0-\\mathbf{X}^{T} \\boldsymbol{y}-\\mathbf{X}^{T} \\boldsymbol{y}+\\left(\\mathbf{X}^{T} \\mathbf{X}+\\mathbf{X}^{T} \\mathbf{X}\\right) \\hat{\\boldsymbol{w}}} \\\\ {\\frac{\\partial E}{\\partial \\hat{\\boldsymbol{w}}}=2 \\mathbf{X}^{T}(\\mathbf{X} \\hat{\\boldsymbol{w}}-\\boldsymbol{y})}\\end{array} 此处推到用到的矢量导数公式: \\frac{\\partial \\boldsymbol{x}^{T} \\boldsymbol{a}}{\\partial \\boldsymbol{x}}=\\frac{\\partial \\boldsymbol{a}^{T} \\boldsymbol{x}}{\\partial \\boldsymbol{x}}=\\boldsymbol{a} 若$\\boldsymbol{X}^{T} \\boldsymbol{X}$为满秩矩阵或正定矩阵,令$\\frac{\\partial E}{\\partial \\hat{\\boldsymbol{w}}}=0$,得: \\hat{\\boldsymbol{w}}^{*}=\\left(\\mathbf{X}^{\\mathrm{T}} \\mathbf{X}\\right)^{-1} \\mathbf{X}^{\\mathrm{T}} \\boldsymbol{y}3、正则化回归现实中$\\boldsymbol{X}^{T} \\boldsymbol{X}$往往不是满秩矩阵,比如$\\boldsymbol{X}$的列数多于行数,此时可能会有多个$\\hat{\\boldsymbol{w}}^{*}$。这时候可以引入正则化项: \\hat{\\boldsymbol{w}}^{*}=\\underset{\\boldsymbol{\\hat { w }}}{\\arg \\min }(\\boldsymbol{y}-\\mathbf{X} \\hat{\\boldsymbol{w}})^{\\mathrm{T}}(\\boldsymbol{y}-\\mathbf{X} \\hat{\\boldsymbol{w}}) + \\lambda ||\\hat{\\boldsymbol w}||^2其中$||\\hat{\\boldsymbol w}||^2=\\sum w_{i}^{2}$,可解得: \\hat{\\boldsymbol{w}}^{*}=\\left(\\mathbf{X}^{\\mathrm{T}} \\mathbf{X} + \\lambda \\boldsymbol I \\right)^{-1} \\mathbf{X}^{\\mathrm{T}} \\boldsymbol{y}$\\boldsymbol I$为单位矩阵,相当于为$\\boldsymbol{X}^{T} \\boldsymbol{X}$的对角线元素增加了$\\lambda$,增强矩阵求逆数值的稳定性。此形式的正则化回归称为岭回归(ridge regression)。如果将$||\\hat{\\boldsymbol w}||^2$(L2正则化)换成$|\\hat{\\boldsymbol w}|$(L1正则化),则称为lasso(least absolute shrinkage and selection operator),lasso对$\\lambda$非常敏感,可以得到稀疏解,但lasso没有解析解。 二、线性判别分析LDA(Linear Discriminant Analysis)LDA基本思想:将样本投影到一条直线上,使同类样本的投影点尽可能近,异类样本投影点尽可能远;在对新样本进行分类时,将其投影到这条直线上,根据投影点的位置确定类别。 1、两类LDA给定数据集$D=\\left\\{\\left(\\boldsymbol{x}_{i}, y_{i}\\right)\\right\\}_{i=1}^{m}$, $y_{i} \\in\\{0,1\\}$,令$X_{i}, \\boldsymbol{\\mu}_{i}, \\boldsymbol{\\Sigma}_{i}$分别为第$i$类的集合、均值向量和协方差矩阵,投影到直线$\\boldsymbol w$上,则两类中心在直线上的投影分别为$\\boldsymbol w^{\\mathrm{T}} \\boldsymbol \\mu_{0}$和$\\boldsymbol w^{\\mathrm{T}} \\boldsymbol \\mu_{1}$;协方差分别为$\\boldsymbol w^{\\mathrm{T}} \\boldsymbol \\Sigma_{0} \\boldsymbol w$和$\\boldsymbol w^{\\mathrm{T}} \\boldsymbol \\Sigma_{1} \\boldsymbol w$。 要使同类投影点尽可能近,则$\\boldsymbol w^{\\mathrm{T}} \\boldsymbol \\Sigma_{0} \\boldsymbol w + \\boldsymbol w^{\\mathrm{T}} \\boldsymbol \\Sigma_{1} \\boldsymbol w$要尽可能小。 要使异类投影点尽可能远,则$\\left|\\boldsymbol{w}^{\\mathrm{T}} \\boldsymbol{\\mu}_{0}-\\boldsymbol{w}^{\\mathrm{T}} \\boldsymbol{\\mu}_{1}\\right|_{2}^{2}$要尽可能大。 同时考虑两者,可得最大化的目标函数: \\begin{aligned} J &=\\frac{\\left\\|\\boldsymbol{w}^{\\mathrm{T}} \\boldsymbol{\\mu}_{0}-\\boldsymbol{w}^{\\mathrm{T}} \\boldsymbol{\\mu}_{1}\\right\\|_{2}^{2}}{\\boldsymbol{w}^{\\mathrm{T}} \\boldsymbol{\\Sigma}_{0} \\boldsymbol{w}+\\boldsymbol{w}^{\\mathrm{T}} \\boldsymbol{\\Sigma}_{1} \\boldsymbol{w}} \\\\ &=\\frac{\\boldsymbol{w}^{\\mathrm{T}}\\left(\\boldsymbol{\\mu}_{0}-\\boldsymbol{\\mu}_{1}\\right)\\left(\\boldsymbol{\\mu}_{0}-\\boldsymbol{\\mu}_{1}\\right)^{\\mathrm{T}} \\boldsymbol{w}}{\\boldsymbol{w}^{\\mathrm{T}}\\left(\\boldsymbol{\\Sigma}_{0}+\\boldsymbol{\\Sigma}_{1}\\right) \\boldsymbol{w}} \\end{aligned} 定义类内散度矩阵: \\begin{aligned} \\mathbf{S}_{w} &=\\boldsymbol{\\Sigma}_{0}+\\boldsymbol{\\Sigma}_{1} \\\\ &=\\sum_{\\boldsymbol{x} \\in X_{0}}\\left(\\boldsymbol{x}-\\boldsymbol{\\mu}_{0}\\right)\\left(\\boldsymbol{x}-\\boldsymbol{\\mu}_{0}\\right)^{\\mathrm{T}}+\\sum_{\\boldsymbol{x} \\in X_{1}}\\left(\\boldsymbol{x}-\\boldsymbol{\\mu}_{1}\\right)\\left(\\boldsymbol{x}-\\boldsymbol{\\mu}_{1}\\right)^{\\mathrm{T}} \\end{aligned} 定义类间散度矩阵: \\mathbf{S}_{b}=\\left(\\boldsymbol{\\mu}_{0}-\\boldsymbol{\\mu}_{1}\\right)\\left(\\boldsymbol{\\mu}_{0}-\\boldsymbol{\\mu}_{1}\\right)^{\\mathrm{T}} 可重新定义目标函数: J=\\frac{\\boldsymbol{w}^{\\mathrm{T}} \\mathbf{S}_{b} \\boldsymbol{w}}{\\boldsymbol{w}^{\\mathrm{T}} \\mathbf{S}_{w} \\boldsymbol{w}}LDA即是最大化$\\mathbf{S}_{b}$和$\\mathbf{S}_{w}$的广义瑞利商。 瑞利商: R(A, x)=\\frac{x^{H} A x}{x^{H} x}$x$为非零向量,$A$为$n \\times n$的Hermitan矩阵,满足$A^{H}=A$,即共轭转置矩阵和自己相等。如果A是实矩阵,则满足$A^{T}=A$的为Hermitan矩阵。瑞利商有一个非常重要的性质,即它的最大值等于矩阵A最大的特征值,而最小值等于矩阵A的最小的特征值,也就是满足 \\lambda_{\\min } \\leq \\frac{x^{H} A x}{x^{H} x} \\leq \\lambda_{\\max }广义瑞利商是指这样的函数$R(A, B, x)$: R(A, B, x)=\\frac{x^{H} A x}{x^{H} B x}其中$x$为非零向量,而$A$,$B$为$n \\times n$的Hermitan矩,$B$为正定矩阵。 若$\\boldsymbol{w}$是一个解,那么对于任意常数$\\alpha$,$\\alpha \\boldsymbol{w}$也是$J$的解,因此$J$的解与$\\boldsymbol{w}$的长度无关,只与其方向有关。可以令$\\boldsymbol{w}^{T} \\boldsymbol{S}_{w} \\boldsymbol{w}=1$,则最大化$J$等价于: \\begin{array}{cl}{\\min _{\\boldsymbol{w}}} & {-\\boldsymbol{w}^{\\mathrm{T}} \\mathbf{S}_{b} \\boldsymbol{w}} \\\\ {\\text { s.t. }} & {\\boldsymbol{w}^{\\mathrm{T}} \\mathbf{S}_{w} \\boldsymbol{w}=1}\\end{array}由朗格朗日乘子,上式等价于: \\mathbf{S}_{b} \\boldsymbol{w}=\\lambda \\mathbf{S}_{w} \\boldsymbol{w}由于$\\mathbf{S}_{b} \\boldsymbol{w}=\\left(\\boldsymbol{\\mu}_{0}-\\boldsymbol{\\mu}_{1}\\right)\\left(\\boldsymbol{\\mu}_{0}-\\boldsymbol{\\mu}_{1}\\right)^{\\mathrm{T}} \\boldsymbol{w}$,$\\left(\\boldsymbol{\\mu}_{0}-\\boldsymbol{\\mu}_{1}\\right)^{\\mathrm{T}} \\boldsymbol{w}$为标量,所以$\\mathbf{S}_{b} \\boldsymbol{w}$的方向恒为$\\boldsymbol{\\mu}_{0}-\\boldsymbol{\\mu}_{1}$。不妨设$\\mathbf{S}_{b} \\boldsymbol{w}=\\lambda \\left(\\boldsymbol{\\mu}_{0}-\\boldsymbol{\\mu}_{1}\\right)$,可得: \\boldsymbol{w}=\\mathbf{S}_{w}^{-1}\\left(\\boldsymbol{\\mu}_{0}-\\boldsymbol{\\mu}_{1}\\right)推论:当两类数据同先验、满足高斯分布且协方差相等时,LDA可达到最优分类。 2、多分类情况LDA推广到多分类任务,设有$N$个类,第$i$类样本数目为$m_{i}$。 重新定义类内散度矩阵: \\mathbf{S}_{w}=\\sum_{i=1}^{N} \\mathbf{S}_{w_{i}} 其中:$\\mathbf{S}_{w_{i}}=\\sum_{\\boldsymbol{x} \\in X_{i}}\\left(\\boldsymbol{x}-\\boldsymbol{\\mu}_{i}\\right)\\left(\\boldsymbol{x}-\\boldsymbol{\\mu}_{i}\\right)^{\\mathrm{T}}$ 定义全局散度矩阵: \\begin{aligned} \\mathbf{S}_{t} &=\\sum_{i=1}^{m}\\left(\\boldsymbol{x}_{i}-\\boldsymbol{\\mu}\\right)\\left(\\boldsymbol{x}_{i}-\\boldsymbol{\\mu}\\right)^{\\mathrm{T}}\\\\ &=\\sum_{j=1}^{N} \\sum_{\\boldsymbol{x} \\in X_{j}}\\left(\\boldsymbol{x}-\\boldsymbol{\\mu}_{j}+\\boldsymbol{\\mu}_{j}-\\boldsymbol{\\mu}\\right)\\left(\\boldsymbol{x}-\\boldsymbol{\\mu}_{j}+\\boldsymbol{\\mu}_{j}-\\boldsymbol{\\mu}\\right)^{T} \\\\ &=\\sum_{j=1}^{N} \\sum_{\\boldsymbol{x} \\in X_{j}}\\left(\\boldsymbol{x}-\\boldsymbol{\\mu}_{j}\\right)\\left(\\boldsymbol{x}-\\boldsymbol{\\mu}_{i}\\right)^{T}+\\sum_{j=1}^{N} \\sum_{\\boldsymbol{x} \\in X_{j}} \\left(\\boldsymbol{\\mu}_{j}-\\boldsymbol{\\mu}\\right)\\left(\\boldsymbol{\\mu}_{j}-\\boldsymbol{\\mu}\\right)^{T} \\\\ &+\\sum_{j=1}^{N} \\sum_{\\boldsymbol{x} \\in X_{j}} \\left(\\boldsymbol{x}-\\boldsymbol{\\mu}_{j}\\right)\\left(\\boldsymbol{\\mu}_{j}-\\boldsymbol{\\mu}\\right)^{T}+\\sum_{j=1}^{N} \\sum_{\\boldsymbol{x} \\in X_{j}} \\left(\\boldsymbol{\\mu}_{j}-\\boldsymbol{\\mu}\\right)\\left(\\boldsymbol{x}-\\boldsymbol{\\mu}_{j}\\right)^{T} \\end{aligned}其中$\\sum_{j=1}^{N} \\sum_{\\boldsymbol{x} \\in X_{j}}\\left(\\boldsymbol{x}-\\boldsymbol{\\mu}_{j}\\right)\\left(\\boldsymbol{x}-\\boldsymbol{\\mu}_{i}\\right)^{T}=\\mathbf{S}_{w}$,$\\sum_{j=1}^{N} \\sum_{\\boldsymbol{x} \\in X_{j}} \\left(\\boldsymbol{\\mu}_{j}-\\boldsymbol{\\mu}\\right)\\left(\\boldsymbol{\\mu}_{j}-\\boldsymbol{\\mu}\\right)^{T}=\\mathbf{S}_{b}$,$\\sum_{j=1}^{N} \\sum_{\\boldsymbol{x} \\in X_{j}} \\left(\\boldsymbol{x}-\\boldsymbol{\\mu}_{j}\\right)\\left(\\boldsymbol{\\mu}_{j}-\\boldsymbol{\\mu}\\right)^{T}=\\sum_{j=1}^{N} \\sum_{\\boldsymbol{x} \\in X_{j}} \\left(\\boldsymbol{\\mu}_{j}-\\boldsymbol{\\mu}\\right)\\left(\\boldsymbol{x}-\\boldsymbol{\\mu}_{j}\\right)^{T}=0$,因此: \\begin{aligned} \\mathbf{S}_{t} &=\\mathbf{S}_{b}+\\mathbf{S}_{w}\\end{aligned} 重新定义类间散度矩阵: \\begin{aligned} \\mathbf{S}_{b} &=\\mathbf{S}_{t}-\\mathbf{S}_{w} \\\\ &=\\sum_{i=1}^{N} m_{i}\\left(\\boldsymbol{\\mu}_{i}-\\boldsymbol{\\mu}\\right)\\left(\\boldsymbol{\\mu}_{i}-\\boldsymbol{\\mu}\\right)^{\\mathrm{T}} \\end{aligned}最优化目标函数可写为: \\max _{\\mathbf{W}} \\frac{\\operatorname{tr}\\left(\\mathbf{W}^{\\mathrm{T}} \\mathbf{S}_{b} \\mathbf{W}\\right)}{\\operatorname{tr}\\left(\\mathbf{W}^{\\mathrm{T}} \\mathbf{S}_{w} \\mathbf{W}\\right)}其中$\\mathbf{W}=\\left[\\boldsymbol{w}_{1}, \\boldsymbol{w}_{2}, \\dots, \\boldsymbol{w}_{i}, \\dots, \\boldsymbol{w}_{k}\\right]$,$\\boldsymbol{w}_{i}$为d行1列的列向量,则: \\left\\{\\begin{array}{c}{\\operatorname{tr}\\left(\\mathbf{W}^{T} \\mathbf{S}_{b} \\mathbf{W}\\right)=\\sum_{i=1}^{k} \\boldsymbol{w}_{i}^{T} \\boldsymbol{S}_{b} \\boldsymbol{w}_{i}} \\\\ {\\operatorname{tr}\\left(\\mathbf{W}^{T} \\boldsymbol{S}_{w} \\mathbf{W}\\right)=\\sum_{i=1}^{k} \\boldsymbol{w}_{i}^{T} \\boldsymbol{S}_{w} \\boldsymbol{w}_{i}}\\end{array}\\right.上式可以通过如下广义特征值问题求解: \\mathbf{S}_{b} \\mathbf{W}=\\lambda \\mathbf{S}_{w} \\mathbf{W}将$\\mathbf{W}$视为一个投影矩阵,则多分类LDA将样本投影到$k$维空间,$k$通常远小于样本维度$d$。LDA也经常被视为一种监督降维方法。 参考资料 周志华《机器学习》 [英]Peter Flach《机器学习》 吴恩达《机器学习》公开课 https://datawhalechina.github.io/pumpkin-book/#/ https://blog.csdn.net/wyl1813240346/article/details/78548274 https://blog.csdn.net/Oxalis_Triangularis/article/details/47420521","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"github","slug":"github","permalink":"http://yoursite.com/tags/github/"},{"name":"机器学习","slug":"机器学习","permalink":"http://yoursite.com/tags/机器学习/"},{"name":"线性模型","slug":"线性模型","permalink":"http://yoursite.com/tags/线性模型/"}]},{"title":"数据结构与算法——链表","slug":"数据结构与算法——链表","date":"2019-06-10T06:35:31.000Z","updated":"2019-08-16T10:03:30.000Z","comments":true,"path":"2019/06/10/数据结构与算法——链表/","link":"","permalink":"http://yoursite.com/2019/06/10/数据结构与算法——链表/","excerpt":"","text":"一、单链表表域元素val保存着数据项,链接域next保存同一表里的下个节点的标识,P为表头变量/表头指针 1、插入新元素 初始化新节点cur 将cur的next字段链接到prev的下个节点 将prev中的next字段链接到cur 2、在开头插入节点 初始化一个新节点cur 将新节点链接到原始头结点head 将cur指定为head 3、删除操作 找到cur的上一个结点prev及其下一个结点next 链接prev到cur的下一个结点next 在第一步中,要找到prev和next,需要从头结点开始遍历链表,才能找到prev,平均时间复杂度是$O(n)$ 4、删除第一个节点 给定原始链表和头结点head 将头结点的下个节点指定为头结点head 5、单链表操作的复杂度 创建链表:$O(1)$ 删除链表:在python里是$O(1)$ 判断表空:$O(1)$ 首端加入元素:$O(1)$ 尾端加入元素,要找到表的最后节点:$O(n)$ 定位插入元素:$O(n)$ 首端删除元素:$O(1)$ 尾端删除元素:$O(n)$ 定位删除元素:$O(n)$ 扫描、定位、遍历:$O(n)$ 6、循环单链表在链表对象里记录尾节点,这样可以同时支持$O(1)$的表头/表尾插入和$O(1)$的表头删除 二、双链表双链表除了next字段,还有一个prev字段,指向当前节点的前一个节点 1、添加操作 初始化一个新节点cur 链接cur与prev的next,链接cur的prev与prev 用cur重新链接prev和next 2、删除操作 3、循环双链表 三、代码实现1、单链表单链表中的节点应该具有两个属性:val 和 next。val 是当前节点的值,next 是指向下一个节点的指针/引用。假设链表中的所有节点都是 0-index 的。在链表类中实现这些功能: get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。 addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。 addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。 addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。 deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。 # 节点类,代表链表的每个节点 class Node(object): def __init__(self, x, next_=None): # 节点的值 self.val = x # 节点的下个指向 self.next = next_ # 链表类 class MyLinkedList(object): # 初始化 def __init__(self): # 初始化头部节点 self.head = None # 记录链表长度 self.length = 0 # 打印链表所有节点 def print_all(self): p = self.head res = [] res.append(p.val) while p.next: p = p.next res.append(p.val) print(res) # 根据索引index获得节点 def get(self, index): \"\"\" 获得第index个节点 \"\"\" # 头部节点 p = self.head # 若链表为空或index小于0,则返回-1 if not p or index < 0: return -1 # 遍历链表找到第index个节点 while index >= 1: # 若index大于链表长度 if p.next == None: return -1 else: p = p.next index -= 1 return p.val def addAtHead(self, val): \"\"\" 在头部添加节点 \"\"\" # 新建值为val的节点,next指向head节点的next,然后将此节点指定为head节点 self.head = Node(val, self.head) self.length += 1 def addAtTail(self, val): \"\"\" 在尾部添加节点 \"\"\" # 如果链表为空,则在头部添加节点 if self.head == None: self.head = Node(val) self.length += 1 return # 链表不为空,先获得头部节点 p = self.head # 遍历节点,找到最后一个节点 while p.next is not None: p = p.next # 最后一个节点指向新建节点 p.next = Node(val) self.length += 1 def addAtIndex(self, index, val): \"\"\" 在第index插入节点。若index等于链表长度,则在尾部添加,若大于节点长度,则节点不会被插入 \"\"\" # 若index等于链表长度,节点添加为尾部 if index == self.length: self.addAtTail(val) # 若index==0,节点添加在头部 elif index == 0: self.addAtHead(val) # 若index大于链表长度或为负,不插入节点 elif index + 1 > self.length or index < 0: return else: p = self.head # 获得插入位置的前一个节点 while index > 1: index -= 1 p = p.next # 新建当前节点,next指向前一个节点的next cur = Node(val, p.next) # 前一个节点的next指向当前节点 p.next = cur self.length += 1 def deleteAtIndex(self, index): \"\"\" 删除第index个节点 \"\"\" # 若index大于链表长度或为负,不删除节点 if index + 1 > self.length or index < 0: return # 若index==0,删除头部节点 elif index == 0: # 重新指定头部节点为当前头部的next self.head = self.head.next self.length -= 1 return else: p = self.head # 获得指定位置的前一个节点 while index > 1: p = p.next index -= 1 # 若前一个节点不为倒数第二个节点(也就是删除除头结点、尾节点以外的中间节点) if p.next.next is not None: p.next = p.next.next # 若指定位置的前一个节点为倒数第二个节点(也就是删除尾节点) else: p.next = None self.length -= 1 2、双指针应用(1)判断环形链表给定一个链表,判断链表中是否有环。为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。 示例1: 输入:head = [3,2,0,-4], pos = 1 输出:true 解释:链表中有一个环,其尾部连接到第二个节点。 示例2: 输入:head = [1,2], pos = 0 输出:true 解释:链表中有一个环,其尾部连接到第一个节点。 示例3: 输入:head = [1], pos = -1 输出:false 解释:链表中没有环。 # Definition for singly-linked list. # class ListNode(object): # def __init__(self, x): # self.val = x # self.next = None class Solution(object): def hasCycle(self, head): \"\"\" :type head: ListNode :rtype: bool \"\"\" ''' # 方法一 双链表 p1 = p2 = head while p2 and p2.next: p1 = p1.next p2 = p2.next.next if p1 == p2: return True return False ''' ### 方法二 哈希表 if head==None: return False hash_table=set() q=head while head: if head in hash_table: return True else: hash_table.add(head) ##set 没有append 只有add head=head.next return False (2)环形链表(二)给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。说明:不允许修改给定的链表。 示例一: 输入:head = [3,2,0,-4], pos = 1 输出:tail connects to node index 1 解释:链表中有一个环,其尾部连接到第二个节点。 示例二: 输入:head = [1,2], pos = 0 输出:tail connects to node index 0 解释:链表中有一个环,其尾部连接到第一个节点。 示例三: 输入:head = [1], pos = -1 输出:no cycle 解释:链表中没有环。 # Definition for singly-linked list. # class ListNode(object): # def __init__(self, x): # self.val = x # self.next = None class Solution(object): def detectCycle(self, head): \"\"\" :type head: ListNode :rtype: ListNode \"\"\" p1 = p2 = head while p2 and p2.next: p1 = p1.next p2 = p2.next.next if p1 == p2: p = head while p != p1: p = p.next p1 = p1.next return p return None (3)相交链表编写一个程序,找到两个单链表相交的起始节点。 示例一: 输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3 输出:Reference of the node with value = 8 输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。 示例二: 输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1 输出:Reference of the node with value = 2 输入解释:相交节点的值为 2 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。 示例三: 输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2 输出:null 输入解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。 解释:这两个链表不相交,因此返回 null。 注意: 如果两个链表没有交点,返回 null. 在返回结果后,两个链表仍须保持原有的结构。 可假定整个链表结构中没有循环。 程序尽量满足 O(n) 时间复杂度,且仅用 O(1) 内存。 # Definition for singly-linked list. # class ListNode(object): # def __init__(self, x): # self.val = x # self.next = None class Solution(object): def getIntersectionNode(self, headA, headB): \"\"\" :type head1, head1: ListNode :rtype: ListNode \"\"\" ''' # 方法一 哈希表 pa, pb = headA, headB table = set() while pa: if pa not in table: table.add(pa) pa = pa.next while pb: if pb in table: return pb pb = pb.next return None ''' # 方法二 双指针 pA = headA pB = headB while pA != pB: pA = pA.next if pA is not None else headB pB = pB.next if pB is not None else headA return pA (4)删除链表的导数第N个节点给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。 示例: 给定一个链表: 1->2->3->4->5, 和 n = 2. 当删除了倒数第二个节点后,链表变为 1->2->3->5. 说明: 给定的 n 保证是有效的。 # Definition for singly-linked list. # class ListNode(object): # def __init__(self, x): # self.val = x # self.next = None class Solution(object): def removeNthFromEnd(self, head, n): \"\"\" :type head: ListNode :type n: int :rtype: ListNode \"\"\" ''' p = head length = 0 table = [] while p: table.append(p) length += 1 p = p.next if n == 1: if length == 1: return None else: table[-n-1].next = None return head if n == length: return table[-n+1] table[-n-1].next = table[-n-1].next.next return head ''' #构建双指针p1与p2,p1先走n步,然后一同运动,当p1指向表尾,p2指向的next即是倒数第N个节点,删除即可。 dummy = ListNode(0) dummy.next = head fast = slow = dummy for i in range(n): fast = fast.next while fast.next: fast = fast.next slow = slow.next slow.next = slow.next.next return dummy.next 参考资料 https://leetcode-cn.com/explore/learn/card/linked-list/ 《数据结构与算法:Python语言描述》","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"算法","slug":"算法","permalink":"http://yoursite.com/tags/算法/"},{"name":"数据结构","slug":"数据结构","permalink":"http://yoursite.com/tags/数据结构/"}]},{"title":"机器学习——模型评估与选择","slug":"机器学习——模型评估与选择","date":"2019-06-06T15:28:53.000Z","updated":"2019-08-16T09:35:24.000Z","comments":true,"path":"2019/06/06/机器学习——模型评估与选择/","link":"","permalink":"http://yoursite.com/2019/06/06/机器学习——模型评估与选择/","excerpt":"","text":"一、偏差与方差设测试样本为$\\bf x$,$y_D$为$\\bf x$在数据集中的标签,$y$为$\\bf x$的真实标签,$f$为训练集$D$上学习得到的模型,$f({\\bf x};D)$为$f$在$\\bf x$上的预测输出。 1、期望预测\\overline{f}({\\bf x})=\\mathbb{E}_{D}[f({\\bf x} ; D)]2、方差使用样本数目相同的不同训练集产生的方差: \\operatorname{var}(\\boldsymbol{x})=\\mathbb{E}_{D}\\left[(f(\\boldsymbol{x} ; D)-\\overline{f}(\\boldsymbol{x}))^{2}\\right]3、噪声\\varepsilon^{2}=\\mathbb{E}_{D}\\left[\\left(y_{D}-y\\right)^{2}\\right]4、偏差期望输出与真是标签的差别为偏差: \\operatorname{bias}^{2}(\\boldsymbol{x})=(\\overline{f}(\\boldsymbol{x})-y)^{2}5、期望泛化误差分解期望泛化误差分解,假定噪声期望为0,即$\\mathbb{E}_{D}\\left[y_{D}-y\\right]=0$: E(f ; D)=\\operatorname{bias}^{2}(\\boldsymbol{x})+\\operatorname{var}(\\boldsymbol{x})+\\varepsilon^{2} 泛化误差 = 偏差 + 方差 + 噪声 偏差度量了算法的期望预测与真是结果的偏离程度,刻画了算法本身的拟合能力。 方差度量了通用大小的训练集的变动所导致的学习性能的变化,刻画了数据扰动所造成的影响。 噪声表达了在当前任务上任何算法所能达到的期望泛化误差的下界,刻画了学习问题本身的难度。 偏差-方差分解说明,泛化性能是由算法的能力、数据的充分性和任务本身难度决定的。 6、过拟合与欠拟合 上图中,训练的初始阶段,算法的学习拟合能力不强,训练数据的扰动不足以使学习器产生显著变化,偏差主导泛化错误率,此时算法欠拟合 随着训练程度的增加,算法的拟合能力增强,以至于在训练后期数据的细微扰动都被学习到,方差主导了泛化错误率,此时算法过拟合 8、进一步理解偏差可以理解为训练集的表现,方差可以理解为验证集的表现。训练集的误差大(欠拟合)则偏差高,训练集的误差低但验证集误差大(欠拟合)则方差高。假设练集和验证集来自相同分布,举例说明: 0 1 2 3 4 训练集误差 1% 15% 15% 0.5% 测试集误差 12% 16% 30% 1% 过拟合,高偏差 欠拟合, 高偏差 高偏差、方差 低偏差低方差 ,适度拟合 降低过拟合风险的方法: 获得更多的数据集 降低模型复杂度 正则化方法 集成学习 降低欠拟合风险的方法: 添加新的特征 增加模型的复杂度 减小正则化系数 二、评估方法假设有包含$m$个样本的数据集$D=\\left\\{\\left(\\bf{x}_{1}, y_{1}\\right),\\left(\\bf{x}_{2}, y_{2}\\right), \\ldots, \\left(\\bf{x}_{m}, y_{m}\\right)\\right\\}$,需要从中产生训练集$T$和测试集$S$ 1、留出法(hold-out)将$D$划分为两个互斥的集合,训练集$T$和测试集$S$。 要尽可能保持数据分布的一致性。 单次使用留出法估计的结果不可靠,一般采用若干次随机划分,重复进行多次后取平均值 2、k折交叉验证以3-折交叉验证为例: 为例建学校因样本划分不同而引入的差别,k折交叉验证通常需要随机使用不同的划分重复P次,最终估计结果是P次k折交叉验证结果的均值。 3、自助法数据集$D$中一共有$m$个样本,每次从中随机选一个复制到$D^{\\prime}$中(相当于有放回采样),重复$m$次。这样最后得到一个有$m$个样本的数据集$D^{\\prime}$。这样一部分样本在$D^{\\prime}$中会出现多次,一部分样本则不会出现。假设样本在$m$次采样中不被采到的概率为$\\left(1-\\frac{1}{m}\\right)^{m}$,取极限: \\lim _{m \\mapsto \\infty}\\left(1-\\frac{1}{m}\\right)^{m} \\mapsto \\frac{1}{e} \\approx 0.368约有36.8%的样本未在$D^{\\prime}$中。我们用$D^{\\prime}$作为训练集,$D \\backslash D^{\\prime}$作为测试集,这样约有1/3没在训练集中出现过的样本作为测试集,这样的测试称为包外估计(out-of-bag) 自助法在数据集较小时很有用。但自助法产生的数据集改变了出事数据集的分布,会引入估计偏差。 三、性能度量1、混淆矩阵对于二分类问题,可以将真实标签和预测值得组合划分为真正例(TP),假正例(FP),真反例(TN),假反例(FN)。令TP、FP、TN、FN分别表示对于的样本数目,显然有TP+FP+TN+FN=样本总数。则混淆矩阵表示如下: 2、准确率、精度、召回率 准确率(accuracy): acc=\\frac{TP+TN}{TP+TN+FP+FN} 精度(precision,也叫查准率): P=\\frac{TP}{TP+FP} 召回率(recall,也加查全率): R=\\frac{TP}{TP+FN}精度与召回率是一堆相互矛盾的指标,P高则R偏低,R 高则P偏低 3、$F_{1}$ 分数$F_{1}$分数综合了精度与召回率: F_{1}=\\frac{2 \\times P \\times R}{P+R}$F_{1}$分数有更加一般的形式: F_{\\beta}=\\frac{1}{1+\\beta} \\left(\\frac{1}{P} + \\frac{\\beta^{2}}{R} \\right)$\\beta > 1$时召回率的影响更大,$\\beta < 1$时精度的影响更大。 4、macro与micro对于多分类问题,每两两类别的组合都对应一个混淆矩阵,我们希望在n个二分类的混淆矩阵上综合考察精度和召回率。 先在个混淆矩阵上分别计算P和R,再计算平均值,得到宏查准率(macro-P)、宏查全率(macro-R)、宏$F_{1}$(macro-$F_{1}$) macro P=\\frac{1}{n}\\sum_{i=1}^n P_{i}macro R=\\frac{1}{n}\\sum_{i=1}^n R_{i}macro F_{1}=\\frac{2 \\times macroP \\times macroR}{macroP + macroR} 还可以将各混淆矩阵的对应缘故进行平均,得到$TP$、$FP$、$TN$、$FN$的平均值$\\overline {TP}$、$\\overline {FP}$、$\\overline {TN}$、$\\overline {FN}$,最后得到微查准率(micro-P)、微查全率(micro-R)、微$F_{1}$(micro-F1) micro P=\\frac{\\overline {TP}}{\\overline {TP} + \\overline {FP}}micro R=\\frac{\\overline {TP}}{\\overline {TP} + \\overline {FN}}micro F_{1}=\\frac{2 \\times microP \\times microR}{microP + microR}5、P-R曲线P-R曲线的横轴是召回率,纵轴是精度。机器学习算法可以一般可以输出属于某个类别的概率,P-R曲线上的某一点表示,在某个概率阈值下,大于该阈值的结果判定为正样本,小于该阈值判定为负样本,此时返回结果对应的召回率和精度。整条P-R曲线就是通过将阈值从高到低移动而形成的。 若一个模型的P-R曲线被另一个完全包住,则后者优于前者。若曲线交叉,则看recall=precision的取值,即平衡点,大的则性能优。 6、ROC曲线ROC曲线全称“受试者工作特征(Receiver operating characteristic)”。绘制方法与P-R曲线一样,不同的是,ROC曲线的纵轴是真正例率(TPR),横轴是假正例率(FPR): TPR=\\frac{TP}{TP+FN}FPR=\\frac{FP}{TN+FP}ROC曲线还有另外一种绘制方法:假定正样本有P个,负样本有N个,把横轴刻度间隔设为$\\frac{1}{N}$,纵轴刻度间隔设为$\\frac{1}{P}$,再根据模型输出的预测概率对样本从高到低排序,以此遍历样本,从零点开始,每遇到一个正样本就沿纵轴方向绘制一个间隔,每遇到一个负样本就沿横轴方向绘制一个间隔,直到(1, 1)这个点。 若一个模型的ROC曲线被另一个完全包住,则后者优于前者。若有交叉,则用ROC曲线下面积AUC(Aera Under ROCCurve)表示: AUC=\\frac{1}{2} \\sum_{i=1}^{m-1} \\left( x_{i+1} - x_{i}\\right) \\left(y_{i} + y_{i+1} \\right)ROC曲线中可能出现斜线,所以应该用梯形面积公式。 参考资料 周志华《机器学习》 《百面机器学习》 https://github.com/datawhalechina/pumpkin-book https://scikit-learn.org/stable/auto_examples/model_selection/plot_roc.html#sphx-glr-auto-examples-model-selection-plot-roc-py","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"github","slug":"github","permalink":"http://yoursite.com/tags/github/"},{"name":"机器学习","slug":"机器学习","permalink":"http://yoursite.com/tags/机器学习/"},{"name":"模型评估","slug":"模型评估","permalink":"http://yoursite.com/tags/模型评估/"}]},{"title":"数据结构与算法——算法复杂度","slug":"数据结构与算法——算法复杂度","date":"2019-06-03T08:08:33.000Z","updated":"2019-06-03T15:20:00.000Z","comments":true,"path":"2019/06/03/数据结构与算法——算法复杂度/","link":"","permalink":"http://yoursite.com/2019/06/03/数据结构与算法——算法复杂度/","excerpt":"","text":"渐进记号一、五种渐进记号 大$O$记号: 对于给定函数$g(n)$,$f(n)=O(g(n))$:存在正常量$c$和$n_{0}$,使得对所有$n \\geq n_{0}$,有$0 \\leq f(n) \\leq cg(n)$成立。$f(n)$只有一个渐进上界,$f(n)$的阶不高于$g(n)$的阶。 $\\Omega$记号: 对于给定函数$g(n)$,$f(n)=O(g(n))$:存在正常量$c$和$n_{0}$,使得对所有$n \\geq n_{0}$,有$0 \\leq cg(n) \\leq f(n)$成立。$f(n)$只有一个渐进下界,$f(n)$的阶不低于$g(n)$的阶。 $\\Theta$记号: 对于给定函数$g(n)$,$f(n)=\\Theta(g(n))$:存在正常量$c_{1}$,$c_{2}$和$n_{0}$,使得对所有$n \\geq n_{0}$,有$0 \\leq c_{1}g(n) \\leq f(n) \\leq c_{2}g(n)$成立。$\\Theta$记号渐进地给出了一个函数的上界和下界,$f(n)$的阶等于$g(n)$的阶 若$f(n)=O(g(n))$且$f(n)= \\Omega (g(n))$,则$f(n)= \\Theta (g(n))$。 小$o$记号: 对于给定函数$g(n)$,$f(n)=O(g(n))$:存在正常量$c > 0$和$n_{0} > 0$,使得对所有$n \\geq n_{0}$,有$0 \\leq f(n) < cg(n)$成立。$f(n)$只有一个渐进紧确上界,$f(n)$的阶低于$g(n)$的阶。 $\\omega$记号: 对于给定函数$g(n)$,$f(n)=O(g(n))$:存在正常量$c > 0$和$n_{0} > 0$,使得对所有$n \\geq n_{0}$,有$0 \\leq cg(n) < f(n)$成立。$f(n)$只有一个渐进紧确下界,$f(n)$的阶高于$g(n)$的阶。 二、 相关定理 定理1: (1)如果$\\lim_{n \\to +\\infty} \\frac{f(n)}{g(n)}$存在,且为某个常数$c$,则$f(n)= \\Theta (g(n))$(2)如果$\\lim_{n \\to +\\infty} \\frac{f(n)}{g(n)}=0$,则$f(n)= O (g(n))$(3)如果$\\lim_{n \\to +\\infty} \\frac{f(n)}{g(n)}=\\infty$,则$f(n)= \\omega (g(n))$ 由定理1可以导出两个重要结果: (1)多项式函数的阶低于指数函数的阶: n^{d}=o(r^{n}), \\quad r > 1,d > 0(2)对数函数的阶低于幂函数的阶: {\\rm ln}n=o(n^{d}), \\quad d>0 定理2,传递性: 若$f(n)= \\Theta (g(n))$,且$g(n)=\\Theta (h(n))$,则$f(n)=\\Theta(h(n))$。对于大$O$、小$o$、$\\Omega$、$\\omega$同样成立。 定理3: f=O(h), \\quad g=O(h) \\rightarrow f+g=O(h)该性质可推广到有限个函数。算法由有限个步骤构成,若每一步的时间复杂度函数的上界都是$h(n)$,那么算法的时间复杂度函数可以写作$O(h(n))$ 三、相关性质 1、自反性: f(n)=\\Theta (f(n))f(n)=O(f(n))f(n)=\\Omega(f(n)) 2、对称性: $f(n)=\\Theta(g(n))$,当且仅当$g(n)=\\Theta(f(n))$ 3、转置对称性: $f(n)=O(g(n))$当且仅当$g(n)=\\Omega(f(n))$$f(n)=o(g(n))$当且仅当$g(n)=\\omega(f(n))$ 四、几类重要函数 至少指数函数级: $2^{n}$, $3^{n}$, $n!$, $2^{2^{n}}$, $n2^{n}$, $(\\log n)^{\\log n}=n^{\\log \\log n}$ 多项式级: $n$, $n^{2}$, $n {\\rm log}n$, $n^{\\frac{1}{2}}$, $n^{3}$, ${\\rm log}(n!)=\\Theta(n {\\rm log}n)$ 对数多项式级: ${\\rm log}n$, ${\\rm log}^{2}n$, ${\\rm log log}n$, $\\sqrt{\\log n}$ 取整函数 $\\lfloor x \\rfloor$:向下取整,$\\lceil x \\rceil$:向上取整 一些性质: x-1","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"算法","slug":"算法","permalink":"http://yoursite.com/tags/算法/"},{"name":"数据结构","slug":"数据结构","permalink":"http://yoursite.com/tags/数据结构/"}]},{"title":"迁移学习理论","slug":"迁移学习理论","date":"2019-05-30T08:00:39.000Z","updated":"2019-08-16T08:44:12.000Z","comments":true,"path":"2019/05/30/迁移学习理论/","link":"","permalink":"http://yoursite.com/2019/05/30/迁移学习理论/","excerpt":"","text":"本文解读的是迁移学习的第一篇理论性的文章:《Analysis of Representation for Domain Adaptation》。笔者水平有限,对于解读有错误或不合理的地方还请批评指正! 一般的机器学习都假设训练集和测试集来自同一分布,但实际上在大多数情况下,我们有带标签的源域数据集(目标域不带标签,或者很少部分带标签),希望能训练一个分类器在一个分布不同的目标域上也能取得很好的泛化能力,这就是domain adaptation领域自适应(以下简称DA)。在什么情况下我们能调整一个在源域上训练的分类器,使其能用在目标域上?从直觉上想,一个好的特征表示是关键因素。这篇文献从理论上形式化了这种直觉,给出了domain adaptation的泛化边界,用到了PAC计算学习理论。这篇文献关注的是目标域无标签的情况。 0. 相关符号含义$\\chi$:样本实例集合 $\\cal Z$:特征空间 $\\cal D$:在$\\chi$上的一个分布 $f$:$\\chi \\to [0, 1]$,$f(x)$代表$x$的标签是1的概率 $\\cal R$:表示函数,将实例映射到特征$\\cal R: \\cal X \\to \\cal Z$ ${\\cal D}_S$:源域样本分布 ${\\cal \\tilde{D}}_S$:源域样本在特征空间$\\cal Z$上的感应(induced)分布 ${\\cal D}_T$:目标域样本分布 ${\\cal \\tilde{D}}_T$:目标域样本在特征空间$\\cal Z$上的感应分布 $h$:预测函数,从特征空间$\\cal Z$到$[0, 1]$ $\\epsilon_S(h)$:$h$关于$\\cal D_T$的期望误差 ${\\rm sup}$:上确界 ${\\rm inf}$:下确界 1. 一般学习问题与DA的形式化1.1 学习问题的定义一个学习问题被两个参数所确定:在$\\chi$上的一个分布$\\cal D$,和目标函数(可能是随机的)$f: {\\cal X} \\to [0, 1]$(本文考虑二分类问题)。表示函数将样本实例映射到特征空间,$\\cal R: \\cal X \\to \\cal Z$。表示函数指示了在$\\cal Z$上的一个分布,从$\\cal Z$到$[0, 1]$的目标函数$\\tilde{f}(z)$如下所示: Pr_\\tilde{\\cal D}[B]=Pr_ {\\cal D} [{\\cal R ^ {-1}} (B)]\\tilde{f}(z)=E_{\\cal D}[f(x)|{\\cal R}(x)=z]对于任意的$A\\subseteq Z$,使得${\\cal R ^ {-1}} (B)$是$\\cal D$可测量的。也就是说,在分布$\\tilde{\\cal D}$下的事件$B$的概率,是在分布$\\cal D$下,$B$在表示函数$\\cal R$的逆下的概率。$z$的标签是1的概率,也就是$\\tilde{f}(z)$的值,是用$z$表示的实例$x$的概率的平均值(期望)。注意,即使$f(x)$不是随机函数,$\\tilde{f}(z)$也可能是随机函数,这是因为表示函数$\\cal R$可以将不同的被$f$标记实例映射到同一个特征表示。 总结一下,这里的学习问题是这样定义的:有个固定的但未知的分布$\\cal D$和目标函数$f$,我们选择一个合适的表示函数$\\cal R$和合适的假设类${\\cal H} \\subseteq \\lbrace g: {\\cal Z}\\to \\lbrace0, 1\\rbrace \\rbrace$,去估计标记函数$f$。 1.2 DA问题的定义“域”的含义是实例集$\\cal X$的分布$\\cal D$。我们假设有两个域,源域(source domain)和目标域(target domain)。${\\cal D}_S$是源域样本分布,${\\cal \\tilde{D}}_S$是源域样本在特征空间$\\cal Z$上的感应分布。同样的含义,我们同${\\cal D}_T$和${\\cal \\tilde{D}}_T$表示目标域。$f: {\\cal X} \\to [0, 1]$是标记规则函数,在源域和目标域上都适用,$\\tilde{\\cal f}$是$f$在$\\cal R$下的感应。 预测函数$h$将特征空间$\\cal Z$映射到$[0, 1]$。我们用$\\epsilon_S(h)$表示$h$和$f$不一致的概率: \\begin{aligned} \\epsilon_{S}(h) &=\\mathrm{E}_{\\mathbf{z} \\sim \\tilde{\\mathcal{D}}_{S}}\\left[\\mathrm{E}_{y \\sim \\tilde{f}(\\mathbf{z})}[y \\neq h(\\mathbf{z})]\\right] \\\\ &=\\mathrm{E}_{\\mathbf{z} \\sim \\tilde{\\mathcal{D}}_{S}}|\\tilde{f}(\\mathbf{z})-h(\\mathbf{z})| \\end{aligned}相似地,我们用$\\epsilon_T(h)$表示$h$相对于${\\cal D}_T$的期望误差。 2. DA的泛化边界DA的各种方法适用带标签的源域数据训练分类器,它会在目标域数据集上有泛化误差。这个泛化误差包括两项: 分类器在源域数据集上的误差边界 感应的源域边缘分布${\\cal D}_S$和感应的目标域边缘分布${\\cal D}_T$之间的差异性 文中说一个自然的想法是使用$L_1$或者变分距离来表示这种差异性,但是这种变分距离在实数域分布之间无法通过有限的样本计算得到。作者在这里采用了$\\cal A$-距离来表示这种差异性。给定一个域$\\cal X$和一个$\\cal X$上的子集$\\cal A$,$\\cal D$和${\\cal D}^{\\prime}$是在$\\cal X$上的概率分布,这两个分布的$\\cal A$-距离定义为: $$d_{\\mathcal{A}}\\left(\\mathcal{D}, \\mathcal{D}^{\\prime}\\right)=2 \\sup _{A \\in \\mathcal{A}}\\left|\\operatorname{Pr}_{\\mathcal{D}}[A]-\\operatorname{Pr}_{\\mathcal{D}^{\\prime}}[A]\\right|$$ 为了使用$\\cal A$-距离,我们需要限定$f$的复杂度。如果说下式成立,那么我们说${\\tilde f:{\\cal Z} \\to [0, 1]}$关于${\\tilde {\\cal D}_S}$和${\\tilde {\\cal D}_T}$是$\\lambda$-接近于$\\cal H$的: \\mathop {\\rm inf}_{h \\in {\\cal H}}[\\epsilon_S(h)+\\epsilon_T(h)] \\leq \\lambdaTheorem 1: ${\\cal R}$是从$\\cal X$到$\\cal Z$的表示函数,$\\cal H$是VC维为$d$的假设类空间,如果$m$条随机样本是从应用了${\\cal R}$函数的${\\cal D}_S$分布上独立同分布地采样得到,其标签由$f$标记,那么对于每个$h \\in {\\cal H}$,下式至少以$1-\\delta$的概率成立: \\epsilon_{T}(h) \\leq \\hat{\\epsilon}_{S}(h)+\\sqrt{\\frac{4}{m}\\left(d \\log \\frac{2 e m}{d}+\\log \\frac{4}{\\delta}\\right)}+d_{\\mathcal{H}}\\left(\\tilde{\\mathcal{D}}_{S}, \\tilde{\\mathcal{D}}_{T}\\right)+\\lambda 边界取决于$d_{\\cal H}({\\tilde {\\cal D}_S},{\\tilde {\\cal D}_T})$,这里选择$\\cal A$-距离,因为我们可以从有限个样本中估计它。 Theorem 2: ${\\cal R}$是从$\\cal X$到$\\cal Z$的表示函数,$\\cal H$是VC维为$d$的假设类空间,如果$m$条随机样本是从应用了${\\cal R}$函数的${\\cal D}_S$分布上独立同分布地采样得到,其标签由$f$标记;并且${\\tilde {\\cal U}_S}$和${\\tilde {\\cal U}_T}$是从${\\tilde {\\cal D}_S}$和${\\tilde {\\cal D}_T}$上采样得到的无标签样本,都分别有$m^{\\prime}$个,那么对于每个$h \\in {\\cal H}$,下式至少以$1-\\delta$的概率成立: $$\\epsilon_{T}(h) \\leq \\hat{\\epsilon}_{S}(h)+\\frac{4}{m} \\sqrt{\\left(d \\log \\frac{2 e m}{d}+\\log \\frac{4}{\\delta}\\right)}+\\lambda+d_{\\mathcal{H}}\\left(\\tilde{\\mathcal{U}}_{S}, \\tilde{\\mathcal{U}}_{T}\\right)+4 \\sqrt{\\frac{d \\log \\left(2 m^{\\prime}\\right)+\\log \\left(\\frac{4}{\\delta}\\right)}{m^{\\prime}}}$$ 之前我们假设$\\lambda$对于合理的$\\cal R$是足够小的,所以上式中第一项和第四项是主要的,表示函数$\\cal R$直接影响了他们。第一项是经验训练误差,第四项是域之间的$\\cal A$-距离。我们可以看出一个好的表示函数$\\cal R$对于降低训练误差和$\\cal A$-距离是至关重要的。 3. $\\cal A$-距离的计算前面说明了$\\cal A$-距离是可以通过有限多样本估计出来的。而且$\\cal A$-距离和学习一个分类器$h$紧密相关,这个分类器是区分样本来自于哪个分布。假设我们有两个分别从${\\tilde {\\cal D}_S}$和${\\tilde {\\cal D}_T}$上采样得到的样本集${\\tilde {\\cal U}_S}$和${\\tilde {\\cal U}_T}$,它们分别有${\\tilde m^{\\prime}}$个,分类器$h$就是要将${\\tilde {\\cal U}_S}$和${\\tilde {\\cal U}_T}$区别开。$h$的误差为: $$\\operatorname{err}(\\mathrm{h})=\\frac{1}{2 \\mathrm{m}^{\\prime}} \\sum_{\\mathrm{i}=1}^{2 \\mathrm{m}^{\\prime}}\\left|\\mathrm{h}\\left(\\mathbf{z}_{\\mathrm{i}}\\right)-\\mathrm{I}_{\\mathrm{z}_{\\mathrm{i}} \\in \\tilde{\\mathcal{U}}_{\\mathrm{S}}}\\right|$$ 是在${\\tilde {\\cal U}_S}$上的指示函数。不幸的是,即使是估计任意分布的最优超平面分类器的误差,也是NP难问题。而我们选择通过最小化误差的凸上限来估计最优超平面分类器,则$\\cal A$-距离通过下式计算: $$d_{A}\\left(\\tilde{\\mathcal{U}}_{S}, \\tilde{\\mathcal{U}}_{T}\\right)=2\\left(1-2 \\min _{h^{\\prime} \\in \\mathcal{H}} \\operatorname{err}\\left(\\mathrm{h}^{\\prime}\\right)\\right)$$ 值得注意的是,这并没有为我们提供目标域错误的有效上限,但它仍然为我们提供了有关领域自适应表示的有用方面。 4. 实验这部分作者使用的数据集是语音标记数据集,从金融领域(source domain)到生物领域(target domain)。先选择表示函数$\\cal R$,再用$\\cal R$得到表示训练一个分类器。本文使用的都是线性映射,包括identity映射、随机映射和SCL(structural correspondence learning)。原始数据的维数为$d^{\\prime}$,这里将其映射至$d$维空间。 随机映射使用随机映射矩阵$P \\in {\\Bbb R}^{d \\times d^{\\prime}}$,$P$从${\\cal N}(0, 1)$中独立同分布地采样得到。Johnson-Lindenstrauss定理保证了只要$d$足够大,随机映射可以很好地接近原始高维空间里的距离。SCL时一种启发式DA方法,首先选择一个在所有域中出现频率最高的“中心”特征,然后其他特征用这些“特征”表示,最后使用共现计数矩阵的低秩近似作为投影矩阵P。 实验结果如下两个图所示: 图1(a)是使用SCL后两个域之间的可视化,图1(a)是使用随机映射后两个域的可视化,使用近似线性鉴别器来鉴别不同的域。图1(b)是使用SCL后不同类别的可视化。对比图1(a)和图1(b),关于域的分类器表现得很差,而类别分类器表现得比较好。域分类器表现得差即误差$err(h)$很大,根据前文section3 $\\cal A$-距离的计算公式,那么$\\cal A$-距离很小。类别分类器的表现得好,经验分类误差比较小。这说明SCL找到了一个比较好的表示使得经验分类误差和$\\cal A$-距离都很小。从图2(b)看,identity映射的类别经验分类误差$\\hat{\\epsilon}_{S}(h)$很小(在带标签的训练数据上的经验误差,也就是源域,这里使用Huber loss作为损失,损失越小经验误差越小),但$\\cal A$-距离很大,导致目标域的分类误差比较大(即表格中的Error列)。随机映射的$\\cal A$-距离虽然很小,但经验误差很大,导致目标域分类误差很大。只有SCL的经验误差和$\\cal A$-距离都很小,最后目标域分类误差也是其中最小的。这些结果也正验证了Theorem 2,Theorem 2说$\\epsilon_{T}(h)$取决于公式中的第一项和第四项,即经验训练误差和域之间的$\\cal A$-距离,只有两者皆小才能达到较低的目标域分类误差。而SCL在两者都得到了较小的值,所以它的目标域分类误差是最小的。","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"理论","slug":"理论","permalink":"http://yoursite.com/tags/理论/"},{"name":"迁移学习","slug":"迁移学习","permalink":"http://yoursite.com/tags/迁移学习/"}]},{"title":"我的书单","slug":"我的书单","date":"2019-04-26T06:43:24.000Z","updated":"2019-08-19T10:56:36.000Z","comments":true,"path":"2019/04/26/我的书单/","link":"","permalink":"http://yoursite.com/2019/04/26/我的书单/","excerpt":"","text":"在书的世界中可以找到真正的自我。 下面的书单有自己读过的书,也有正在读,或还没读觉得很不错的书。带评注的是我已经读过的,希望和爱书的大家一起读书交流! 科幻文学 table th:nth-of-type(1) { width: 200px; } table th:nth-of-type(2) { width: 100px; } table th:nth-of-type(3) { width: 300px; } 书名 作者 评注 与罗摩相会 [英]阿瑟·克拉克 克拉克获得雨果奖的作品,在高等文明面前,我们是如此的渺小 沙丘 [美] 弗兰克·赫伯特 海伯利安 (美)丹·西蒙斯 中国文学 书名 作者 评注 红高粱家族 莫言 很早之前就被改编为电影,要读莫言此作品必读 丰乳肥臀 莫言 讴歌生命的意义和母亲的伟大,也揭露了人性险恶的一面 活着 余华 书中的主人公命运非常悲惨,最后沦落为只身一人和一头老牛相依为命,看完这个故事你会觉得好想过完了一生 国外文学 书名 作者 评注 世界诞生于午夜 〔英〕珍·坎贝尔 人生拼图版 [法] 乔治·佩雷克 北欧众神 [英] 尼尔·盖曼 指环王、漫威雷神、冰与火之歌等等,都从北欧神话获得灵感 伊斯坦布尔:一座城市的记忆 [土耳其] 奥尔罕·帕慕克 动物庄园 乔治•奥威尔 政治讽刺小说,动物们把人类赶出庄园,最后却被猪统治的统治毁掉庄园 人类群星闪耀时 茨威格 小王子 [法]安托万·德·圣埃克苏佩里 大人的童话,震撼心灵,希望我们能不失去纯真 人间失格 [日]太宰治 传记 书名 作者 评注 李鸿章传 梁启超 我们不能单单评价李鸿章是卖国贼,梁启超都不同意,实际上李鸿章是最被误读的历史人物之一 张宏杰 历史政治文化 书名 作者 评注 论中国 [美]基辛格 一部中国的老朋友写的中国近现代史 世界秩序 [美]基辛格 一看就停不下来的中国史 最爱君 语言诙谐幽默,有很多非常有意思的历史瞬间和人物,确实一看就停不下来了 拜占庭简史 (英) 乔纳森·哈里斯 谈论欧洲史必然绕不开罗马帝国,一本书浓缩了拜占庭帝国千年历史 思想史 : 从火到弗洛伊德 [英] 彼得·沃森 拜占庭帝国 : 拯救西方文明的东罗马千年史 [美] 拉尔斯·布朗沃思 古诗词课 叶嘉莹 抗日战争的细节 魏风华 描写国军的正面战场,抗日战争远比我们想象得要惨烈的多 科普 书名 作者 评注 众病之王:癌症传 [美] 悉达多·穆克吉 癌症的发现、治疗历史,书写的有些啰嗦 世界咖啡地图 詹姆斯·霍夫曼 一本书带你从咖啡豆的种植到咖啡的生产的全过程,全彩,很精美 超越时空 [美]加莱道雄 现实不似你所见 [意]卡洛·罗韦利 上帝掷骰子吗 曹天元 一本看了就放不下的书!量子力学发展史,难得的科普佳作 经管 书名 作者 薛兆丰经济学讲义 薛兆丰 经济学通识课 [英] 尼尔·基什特尼 重新定义团队 [美]拉斯洛·博克 谷歌方法 [美]比尔•基尔迪 OKR工作法 克里斯蒂娜·沃特克 千年金融史 威廉·戈兹曼 艺术/设计 书名 作者 世界建筑简史 : 9000年的世界标志性建筑 盖纳•艾尔特南 聆听音乐(第五版) [美] 克雷格·莱特 心理 书名 作者 高敏感度的力量 [德] 卡特琳·佐斯特、Kathrin Sohst 情感依附:为何家会影响我的一生 [美]亨利•马西、[美]内森•塞恩伯格 逃避自由 [美] 埃里希·弗罗姆 对伪心理学说不 [加]基思·斯坦诺维奇 画册 书名 作者 评注 莫奈:逐光者 [法] 萨尔瓦·卢比奥 编著、伊法 绘 画的非常精美,讲述了莫奈前半生的创作历程和潦倒的生活 父与子全集 [德] 埃·奥·卜劳恩 绘 遗产 [以] 露图·莫丹 围绕着一份遗产,一段尘封往事将三代人联系在了一起 万物 [德] 延斯·哈德 IT/互联网/工业 书名 作者 评注 浪潮之巅 吴军 IT科技企业的起起伏伏 重新定义公司——谷歌是如何运营的 [美] 埃里克·施密特","categories":[{"name":"随笔","slug":"随笔","permalink":"http://yoursite.com/categories/随笔/"}],"tags":[{"name":"书单","slug":"书单","permalink":"http://yoursite.com/tags/书单/"}]},{"title":"英语学术论文写作","slug":"英语学术论文写作","date":"2019-04-01T03:01:46.000Z","updated":"2021-04-14T09:15:50.000Z","comments":true,"path":"2019/04/01/英语学术论文写作/","link":"","permalink":"http://yoursite.com/2019/04/01/英语学术论文写作/","excerpt":"","text":"本文为《英语科技论文写作(通信、计算机、密码、测绘版)》一书的学习总结。 第一章 概论略 第二章 英语科技论文的文体特点2.1 词汇特点英语科技论文的特点之一就是大量使用科技词汇,主要包括以下几类: 纯科技词汇。如diode(二极管)、database(数据库)等。 通用科技词汇。如power在机械力学中词义为“力”、“电”、“电力”、“动力”、“电源”、“功率”的等,在数学中为“乘方”、“次方”、“幂”。feed一词有“馈电”、“供水”、“输送”、“进刀”等意。 普通词汇专业化 合成词。如software(软件)、hoursepower(马力)等。 混成词。如comsat(通信卫星)、hi-tech(高新技术)等。 派生词。如前缀hydro-,hyper-,hypo-,inter-等。 缩略词。如IC(integrated circuit)集成电路、AI(artificial intelligence)人工智能 语体正式词。动词短语宜用与之对应的、意义明确地单个词的动词。如: 一般语体 正式语体 一般语体 正式语体 use up exhaust 耗尽 push into insert 插入,嵌入 throw back reflecct 反映,反射 put in add 加,增加 carry out perform 执行,完成 think about consider 考虑 get rid of eliminate 消除,排除 take away remove 移动,迁移,调动 get together concentrate 集中,浓缩 fill up occupy 占据,占领 keep up maintain 维持,继续 hang up suspend 延缓,推迟 一般语体 正式语体 一般语体 正式语体 finish complete 完成 underwater submarine 水下的 buy purchase 购买 enough sufficient 足够的 similar identical 同一的,完全相同的 handbook manual 手册,指南 careful cautious 谨慎的,小心的 help assist 帮助,促进 try attempt 企图,试图 get obtain 获得 about approximately 大约,近似地 use utilize 利用 2.2 句法特点 较多使用被动语态 较多使用非谓语动词(分词、动名词、动词不定式及其复合结构)。例如: Compared with DoD’s plan, the options would yield greater improvemnets in reception and would yield improvements sooner. A theoretical framework is provided, consisting of negative reinforcing feedback loops that act as drivers behind future industry change. 大量使用名词化结构(主要是用具有动词意义的名词+of+修饰词)。 非人称句使用频繁(第三人称作主语的矩句子)。 第三章 英语科技论文的语言表达技巧和常见错误3.1 如何提高语言的准确性 选用具体的表达,避免抽象的表达。例如: a1:Our experimenntal data are not precise because the devices are in poor condition.(含糊) a2:Our experimenntal data are not precise because the devices are not working properly.(具体) b1:The results seem to be satisfactory.(含糊) b2:We are satisfied with the result that $L(X2, X3)=0$. (具体) 下列几个词语的相对意义不够确切,尽量少用:in the case of, generally, somewhat, unduly, several, fairly, rather, overall, certain, as such, anticipate, presently等 选用较为正式的动词 区别易混淆词、同义词和近义词。例如: The first result was obtained after approximately four minutes.句中的approximately与about, around, roughly都表示“大约大概”的意思,但approximately比其他词更准确。 近义词举例:’consist of’用主动语态,而compose的用法是“整体+be composed of+组成部分”: a1:This process consists of the translation, reconstruction, and interpretation of three basic elements of the application. a2:Java applications are composed of one or more class files, one file per class. 避免使用引起歧义的词语 3.2 如何提高语言的简洁性3.2.1 使用短语代替从句1、 名词短语代替从句。名词短语可以替代状语从句、宾语从句、表语从句、主语从句、同位语从句。 If the temperature of engines decreases dramatically, the fuel consumption of automobiles will increase.(条件状语从句可以简化为:A dramatic decrease in the tenperature of engines will result in an increase in the fuel consumption of automobiles.) Note that the result is accurate. (宾语从句可以简化为:Note the accuracy of the result.) One of the advantages of the machines is that they are small in size. (表语从句可以简化为:One of the advantages of the machine is their small size.) To resolve the problem that malicious software load more and more harm to computer system, SRN mechanism is put forward. (同位语从句及其先行词可以简化为:To resolve the increasing harm of malicious software on computer system, SRN machanism is put forward.) The value is the highest in the experiment. (可以简化为:The experiment has the highest value.) Check Mate is a program analysis framework for Java. It uses Jchord to instrument lock acquires and releases. (两句话可以合并为:Check Mate, a program analysis framework for Java, uses Jchord to instrument lock acquires and releases.) 2、 形容词短语代替从句。形容词短语不仅可以充当定语,有事还可以替代从句表示原因、状态或补充说明。 The satellites which are capable of transmitting M-code signals were already in orbit as of August 2011. (定语从句可以简化为:Ten satellites capable of transmitting M-code signals were already in orbit as of August 2011.) 3、 副词短语替代从句 As far as the theory and computation are concerned, material anisotropy and layering pose great challenges. (状语从句可以简化为:Material anisotropy and layering pose great challenges both theoretically and computationally.) 4、 介词短语代替从句 It’s a simple and scalable protocol, which performs well for average-case loads and operating conditions. (非限制性定语从句可简化为:with good performance for average-case loads and operating conditions) The communication interface should be rather simple to implement. It dosen’t need specialized communication controllers. (第二句可简化为:without specialized communication controllers) 5、 分词短语代替从句。现在分词表示主动含义,过去分词表示被动含义。 This paper gives an overview of GRASP, and also describes its basic components and enhancements to the basic procedure. (并列动词可简化为:describing its basic components and enhancements to the basic procedure) From the class of protocols that is triggered by event, the controller area network bus is used in many automotive applications. (定语从句可简化为:From the eventtriggered class of protocols,…) 6、 动名词短语替代从句。动名词短语可以替代主语从句、宾语从句,作介词宾语可以替代状语从句,有时也可以吧并列句简化为简单句。 You can transmit F three times clearly, but it doesn’t do the trick. (并列句可简化为:Transmitting F three times clearly doesn’t do the trick.) 7、 不定式短语替代从句,不定式短语经常用来替代目的状语。 3.2.2 变后置定语为前置定语 There was a choice that can not be avoided between privacy and user-desired functionality. (后置定语可简化为:There was an unavoidable choice between privacy and user-desired functionality.) 3.2.3 使用省略手段1、 省略谓语 Scheme 1 got the highest average while Scheme 2 got the lowest. (Scheme 1 got the highest average while Scheme 2 the lowest.) 2、 省略从句中部分成分 When used without a recovery service, Carburizer identifies bugs for a programmer to fix. (省略了it is) When confronted with real devices that misbehave, these assumptions can lead to driver or system failures. (省略了thay are) 3、 利用缩略词 4、 简化习语和词组 词组 简化 词组 简化 aimed at for inasmuch as since a number of several in regard to about in all cases always in order that to in moast cases usually in the event that if in the nature of like not infrequently often for the purpose of for in the vicinity of near in connection with about in spite of the fact that although it may well be that perhaps on a regular basis regularly with the result that so bring to a consideration consider afford an opportunity to allow in conjunction with with after this has been done then the question as to whether whether it is therefore apparent that hence in view of the foregoing statement therefore in view of the fact that…/on account of the fact that …/owing to the fact that … seeing that/because/since it’s evident that… evidently… it would appear that… apparently … it is clear that … clearly 3.3 如何提高语义的连贯性3.3.1 使用连词 表示时间和顺序关系:after, before, first(ly), second(ly), third(ly), last(ly), then, in the first place, to begin with, next, finally, later, afterwards, meanwhile, in the mean time, in the past, at present, presently, ultimately, recently 表示空间关系:here, there, nearby, under, below, in front of, in the middle of, at the back of, on the top of, at the bottom of 表示递增关系:and, also, in addition,additionally, besides, further, furthermore, moreover, what is more 表示转折关系:however, nevertheless, but, although, yet, while, in spite of, instead, now that, whereas 表示相似关系:similarly, equally, likewise, in the same way, in a like manner 表示对比关系:in contrast, alternately, on the contrary, not…but… 表示因果关系:since, because, as a result, due to, thanks to, for this reason, therefore, consequently, accordingly, thus, so, hence 表示让步关系:however, nevertheless, though, on the other hand, yet, still, in any case, after all, anyway 表示列举或重复诉述内容:for example, for instance, such as, namely, in other words, that is to say, that is, e.g., i.e., as before, as has been stated, as discudded previously, again, in review, as shown in Fig.2, to reiterate 表示总结:in conclusion, to sumup, in short, in a word, in brief, briefly speaking, on the whole 3.3.2 使用代词或重复关键词代词的使用既可以避免次重复,使行文简洁,也可以使语句衔接更为紧密。 3.3.3 使用主从结构 a1: So instead of enlarging the line size, we choose to implemnet a fetch size. It is different from thr line size. a2: So instead of enlarging the line size, we choose to implemnet a fetch size which is different from thr line size. 3.3.4 使用平行结构 The SMART-3D architecture achieves speedups from 1.53 to 2.14 over planar designs and from 1.27 to 1.72 over prior 3D designs.(介词短语平行) Therefore, it is very difficult, if not impossible, to compute them both accurately and efficiently.(副词平行) 3.4 英语科技论文中的常见错误3.4.1 数1、 不规则变化的名词单复数 单数 复数 含义 criterion criteria 标准 phenomenon phenomena 现象 medium media 介质 radius radii 半径 index indices 指数 appendix appendices 附录 stimulus stimuli 刺激 basis bases 基础 synthesis syntheses 合成 matrix matrices 矩阵 2、 一些特殊的名词 data通常作复数名词,而research、work是不可数名词 3、 主谓一致 one of 复数名词 + 单数动词 none of 复数名词 + 单数动词 none of 单数名词/不可数名词 + 单数动词 no 复数名词 + 复数动词 a kind of 单数名词 + 单数动词 this/that kind of 单数名词 + 单数动词 these kind of 复数名词 + 复数动词 复数名词 of this/that kind (sort) + 复数动词 a type of 单数名词 + 单数动词 a series of 复数名词 + 复数动词 the number of 复数名词 + 单数动词 a number of 复数名词 + 复数动词 4、 后面通常用复数名词的词 variois,most,all,many,several, one of, many of, kinds of, a number of, the number of, a(wide)variety of, a class of, a series of, a wide range of 5、 修饰可数名词和不可数名词的量词 修饰可数名词的量词有:a few of,many,a number of 修饰不可数名词的量词有:a great/small/certain amount of,agreat/good deal of 修饰可数和不可数名词均可的量词有:a lot of,lots of 6、 以下结构中谓语的数 由靠近谓语动词的名词或代词来决定谓语的数的短语有:not only……but also……, either……or……, neither……nor…… as well as 谓语动词往往用复数 3.4.2 冠词 第一次出现或表示泛指的单数名词前一般要加不定冠词a/an 有修饰词的、特指的或再次出现的单数名词前一般要加定冠词the,但有些情况可以不加 无特指或无修饰词的复数名词前不加定冠词the,特指的或被修饰的复数名词前一般加the 3.4.3 谓语动词与非谓语动词 动名词:由动词原形加后缀ing构成,可做主语、宾语和表语,与介词构成的介词短语(如in/on/without + doing)可做定语、状语 现在分词:由动词原形+ing构成,可做定语、补语和状语,表示主动、正在进行的含义 过去分词:由动词原形+ed构成(规则动词),可做定语、补语,少部分可做状语(如Given……),表示被动的含义 不定式:由动词原形前加to构成,可做主语、宾语、定语、补语和目的状语,表示将来、目的性含义 第四章、第五章 略 第六章 表格、插图、数学公式的撰写一、插图的英文表达 figure,通常指几何图形或图案,不管什么图都可以用figure表示。 diagram,用于一般的图形、图表 graph,一般用于指曲线图、标绘图 plot,往往可以与graph互换 view,一般用于机械图或各种结构图中的各向视图 profile,一般只零部件的外形轮廓图,剖面形状图,也可以指各种分布图 pattern,指图型、图案、花纹等 drawing,主要用于各种工程图纸及有关工程设计的附图、插图 chart,多用于航线图和某种方向的图 map,地图、天体图、布局图及一些专用图形 sketch,一般指粗略勾画的示意图、草图 layout,着重于被描述对象的总体概貌,外形轮廓,总体安排和设计 line,一般仅用于线条描绘的轮廓和外形图 scheme,草图,示意图 二、图表的描述语言1、图表的指示 As shown in Table 1 … is shown in Table 1 Table 1 shows that… Each methods is proved to be effective(Table 1) 2、描述变化或趋势常用词 描述一般变化: to go up (a little) to go down (a little) Nouns Verbs Nouns Verbs an increase to increase a decrease to decrease a rise to rise a fall to fall (off) a growth to grow a drop to drop an improvement to improve a decline to decline an upturn a dowmturn to go up to go down to slip an upward a downward trend 描述剧烈变化: to go up (a lot) to go down (a lot) Nouns Verbs Nouns Verbs a surge to surge to plummet an upsurge a plunge to plunge to take off a slump to slump to shoot up a crash to crash to soar to sink to rocket a tumble to bumble a jump to jump a leap to leap 描述变化结果 no change change of direction downward upward to remain stable to peak to level off to reach a peak to reach a low point to stay at the same level to top out to recover to remain constant to rebound to stagnate to revive to stabilise 描述变化速度 to describe the speed of change objectives adverbs an abrupt rise or fall to rise or fall abruptly a sudden suddenly rapid rapidly quick quickly steady steadily gradual gradually slow slowly 描述变化程度 to describe the degree of change objectives adverbs a dramatic rise of fall to rise or fall dramatically considerable considerably sharp sharply significant significantly substantial substantially moderate moderately slight slightly 3、描述图表的套句 The table shows the changes in the number of … over the period from … to …改表格描述了在…年至…年间…数量的变化 The graph provides some interesting data regrading … 该图为我们提供了有关…的有趣数据 The diagram /bar chart/pie graph/tree diagram/data/figures/statistics … show(s)/illustrate(s)/depict(s) that…该图/数据…向我们展示/揭示/表明了… This is a cure graph which describes the trend of … 这个曲线描述了…的趋势 The data /statistics/figures lead us to the conclusion that…从数据中,我们得出… As is shown/demonstrated/exhibited in the diagram/graph/chart/table … 如图所示… According to the chart/figures…根据这些表格(数字)… As is shown in the table…如表格所示 As can be seen from the diagram, great changes have taken place in … 从图中可以看出,…发生了巨大变化 From the table/chart/diagram/figure, we can see clearly that … or, It is clear/apparent from the chart that…从图表中我们可以清楚看到 This table shows the changing proportion of a & b from … to 该表格描述了 ……年到……年间a与b的比例关系 The graph presented in a pie chart shows the general trend in … 该图形以饼状图描述了…总的趋势 This is a column chart showing … 这是个柱状图,描述了…… As can be seen from the graph, the two curves show the fluctuation of …如图所示,两条曲线描述了……的波动情况 Over the period from … to…, the …remained level. 在……至……期间,……基本不变 in the year between… and …在……年间到……期间 in the 4 years spanning from 1995 through 1998… 在1995至1998四年里 From then on/from this time onwards… 从那时起 The number of … remained steady/stable from (month/year) to (month/year) ……至……的数量基本不变 The number sharply went up to… 数字急剧上升至…… The percentage of … stayed the same between … and … ……至……期间……的比率维持不变 The figures peaked at … in (month/year) … ……的数目在……达到顶点,为…… The percentage remained steady at … 比率维持在…… The percentage of … is slightly larger/smaller than that of … ……的比例比……的比例略高(低) There is not a great deal of different between … and … ……与……的区别不大 The graphs show a threefold increase in the number of … 该图表表明……的数目增长了三倍 …decreased year by year while … increased steadily ……逐年减少,而……逐步增加 The situation reached a peak (a high point at) of …% ……的情况达到顶(高)点,为……百分点 The figures.situation bottomed out in … 数字(情况)在……达到底部 The figures reached the bottom/a low point/hit a trough. 数字(情况)达到底部(低谷) a is … times as much /many as b. a是b的……倍 a increased by/to … a增长了/到…… The difference between a and b lies in … a与b之间的差别在于…… …(year) witnessed/saw a sharp rise in … ……年……急剧上升 The number of … rose steadily, whereas/while that of … fell slightly. ……的数量不断上升,而……的数量不断下降 三、数学公式的搭配用语 中文 英文 证,证明 prove 设,假设 assume; we claim… 令 let 若 if 因,因为,由于 since 则,故,因此,因而 then; thus; therefore; so; hence 对于 for 有 there exists… 即 that is; i.e.; which means… 于是,由此 such that 定义 define 便得,可得,可知 we have…; we obtain…; we can write…; it follows that…; note that…; one can derive that… 以及 and 或者 or 此处,这里,式中,其中 where x为 x is…; x denotes… 由此得 produce; lead to 一般来说 in general; generally 当且仅当,重要条件是 if and only if 式1变为 equatuion 1 becomes… 由式1可得 from equation 1, we get … 可表达为 can be expressed as …; can be given by … 将式1带入式2 insert (1) into (2); apply (1) to (2) 式1左边/右边 the left/right (hand) side of equation 1 满足 subject to; satisfy; obey 如下 in the following; as follows 证明完毕 QED 考虑 if one consider…; consider… 如果……那么…… if … then … 四、数学公式的英文读法$\\exists$: there exists$\\forall$: for all$p \\implies q$: p implies q / if p, then q$q \\iff p$: p if and only if q / p is eqivalent to q / p and q are equivalent$x \\in A$: x belongs to A / x is an element(or a member) of A$x \\notin A$: x does not belong to A / x is not an element(or a member) of A$A \\subset B$: A is contained in B / A is a subset of B$A \\supset B$: A contains B / B is a subset of A$A \\bigcap B$: A cap B / A meet B / A intersection B$A \\bigcup B$: A cup B / A join B / A union B$A \\backslash B$: A minus B / the difference between A and B$A \\times B$: A cross B / the Cartesian product of A and B$x \\equiv y$: x is equivalent to(or identical with) y$x > y$: x is greater than y$x \\geq y$: x is greater than or equal to y$x < y$: x is less than y$x \\leq y$: x is less than equal to y$0<x<1$: zero is less than x is less than one$0 \\leq x \\leq 1$: zero is less than or equal to x is less than or euqal to 1$|x|$: mod x / modulus x$x^{2}$: x squared / x(raised) to the power 2$x^{3}$: x cubed$x^{4}$: x to the fourth / x to the power four$x^{n}$: x to the nth/ x to the power n$x^{-n}$: x to the (power) minus n$\\sqrt{x}$: (square) root x / the square root of x$\\sqrt[3]{x}$: cube root (of) x$\\sqrt[4]{x}$: fourth root (of) x$\\sqrt[n]{x}$: nth root (of) x$(x+y)^{2}$: x plus y all squared$\\left(\\frac{x}{y} \\right)^{2}$: x over y all squared$f:S \\to T$: a function f from S to T$x \\mapsto y$: x maps to y / x is sent (or mapped) to y$f ^\\prime (x)$: f prime x / f dash x / the (first) derivative of f with respect to x$f ^{\\prime \\prime} (x)$: f double-prime x / f double-dash x / the second derivative of f with respect to x$f ^{\\prime \\prime \\prime} (x)$: f triple-prime x / f triple-dash x / the third derivative of f with respect to x$f^{(4)}(x)$: f four x / the fourth derivative of f with respect to x$\\frac{\\partial f}{\\partial x_{1}}$: the partial (derivative) of f with respect to $x_{1}$$\\frac{\\partial f}{\\partial x_{1}^{2}}$: the second partial (derivative) of f with respect to $x_{1}$$\\int_{0}^{\\infty}$: the integral from zero to infinity$\\lim_{n \\to 0}$: the limit as x approaches zero$\\lim_{n \\to +0}$: the limit as x approaches zero from above$\\lim_{n \\to -0}$: the limit as x approaches zero from below$\\log _{e}y$: log y to the base e / log to the base e of y / natural log (of) y$\\ln y$: log y to the base e / log to the base e of y / natural log (of) y","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"论文写作","slug":"论文写作","permalink":"http://yoursite.com/tags/论文写作/"}]},{"title":"机器学习的数学基础:矩阵论","slug":"机器学习的数学基础:矩阵论","date":"2019-03-28T08:41:53.000Z","updated":"2019-04-04T15:49:38.000Z","comments":true,"path":"2019/03/28/机器学习的数学基础:矩阵论/","link":"","permalink":"http://yoursite.com/2019/03/28/机器学习的数学基础:矩阵论/","excerpt":"","text":"1. 基本概念1.1 向量及其转置一个$d$维列向量$x$及其转置$x^t$可记作: x=\\left[ \\begin{matrix} x_1 \\\\ x_2 \\\\ \\vdots\\\\ x_d\\\\ \\end{matrix} \\right]\\ 和 x^t=\\left[ \\begin{matrix} x_1 & x_2 & \\dots & x_d\\\\ \\end{matrix} \\right]\\tag{1}1.2 矩阵及其转置一个$n \\times d$的矩阵$\\mathbf M$及其$d \\times n$的转置矩阵$\\mathbf M^t$为: \\mathbf M=\\left[ \\begin{matrix} m_{11} & m_{12} & \\dots & m_{1d}\\\\ m_{21} & m_{22} & \\dots & m_{2d} \\\\ \\vdots & \\vdots & \\ddots & \\vdots\\\\ m_{n1} & m_{n2} & \\dots & m_{nd}\\\\ \\end{matrix} \\right]\\tag{2}和 \\mathbf M^t=\\left[ \\begin{matrix} m_{11} & m_{21} & \\dots & m_{n1}\\\\ m_{12} & m_{22} & \\dots & m_{n2} \\\\ \\vdots & \\vdots & \\ddots & \\vdots\\\\ m_{1d} & m_{2d} & \\dots & m_{nd}\\\\ \\end{matrix} \\right]\\tag{3}1.3 对称矩阵与反对称矩阵一个$d \\times d$的方阵, 如果其元素满足$m_{ij}=m_{ji}$,则称为对称矩阵;一个的$d \\times d$方阵,如果其元素满足$m_{ij}=-m_{ji}$,则称为斜对称矩阵或反对称矩阵。 1.4 单位矩阵$ \\mathbf I$单位矩阵必须为方阵,其对角线元素均为1,非对角线元素均为0。 1.5 对角矩阵非对角线元素均为0的矩阵,也记作$diag(m_{11}, m_{22}, \\dots, m_{dd})$ 1.6 向量或矩阵相加减向量或矩阵的相加减即为对应元素相加减,参与运算的两个向量或矩阵维数必须相等。 1.7 矩阵与向量相乘矩阵与向量相乘$\\mathbf M \\mathbf x=\\mathbf y$可以表示为: \\left[ \\begin{matrix} m_{11} & m_{21} & \\dots & m_{n1}\\\\ m_{12} & m_{22} & \\dots & m_{n2} \\\\ \\vdots & \\vdots & \\ddots & \\vdots\\\\ m_{1d} & m_{2d} & \\dots & m_{nd}\\\\ \\end{matrix} \\right] \\left[ \\begin{matrix} x_1 \\\\ x_2 \\\\ \\vdots\\\\ x_d \\\\ \\end{matrix} \\right] = \\left[ \\begin{matrix} y_1 \\\\ y_2 \\\\ \\vdots\\\\ y_n \\\\ \\end{matrix} \\right ] \\tag{4}其中:$y_i=\\sum\\limits_{j=0}^d m_{ij}x_j$。注意矩阵的列数必须等于向量的行数。 2. 向量内积与外积2.1 向量内积两个具有相同维数的向量$\\mathbf x$与$\\mathbf y$的内积记为$\\mathbf x^t \\mathbf y$,这是一个标量: \\mathbf x^t \\mathbf y=\\sum\\limits_{i=0}^dx_i y_i=\\mathbf y^t \\mathbf x \\tag{5}内积也称作点积,记为$\\mathbf x \\cdot \\mathbf y$或$\\langle \\mathbf x , \\mathbf y \\rangle$。 2.2 欧几里德范数向量的欧几里德范数即为向量的长度,定义为: \\begin{Vmatrix}\\mathbf x \\end{Vmatrix}= \\sqrt{\\mathbf x^t \\mathbf x} \\tag{6}如果一个向量$\\mathbf x$满足$\\begin{Vmatrix}\\mathbf x \\end{Vmatrix}= 1$,则称这个向量是归一化的。 2.3 两个向量的夹角两个d维向量的夹角为: cos\\theta= \\frac{\\mathbf x^t\\mathbf y}{\\begin{Vmatrix}\\mathbf x \\end{Vmatrix}\\begin{Vmatrix}\\mathbf y \\end{Vmatrix}}\\tag{7}当$\\mathbf x^t\\mathbf y=0$时,这两个向量是正交的。当$\\begin{Vmatrix}\\mathbf x^t \\mathbf y\\end{Vmatrix}= \\begin{Vmatrix}\\mathbf x \\end{Vmatrix}\\begin{Vmatrix}\\mathbf y \\end{Vmatrix}$时,这两个向量是共线的。 由于对于任意角度$\\theta$都有$|cos\\theta|\\leq 1$,由式(7)可得柯西-施瓦茨不等式: |\\mathbf x^t \\mathbf y| \\leq \\begin{Vmatrix}\\mathbf x \\end{Vmatrix} \\begin{Vmatrix}\\mathbf y \\end{Vmatrix}\\tag{8}2.4 线性相关与线性无关对于一组给定的向量$\\lbrace \\mathbf x_1, \\mathbf x_2, \\dots, \\mathbf x_n \\rbrace$,如果其中不存在任何一个能被表示为其余向量的线性组合的向量,那么这组向量是线性无关的,反之则是线性相关。一组d个线性无关的d维向量能够张成一个d维向量空间,也就是说,这个空间中的任意向量都可以表示为这些线性无关向量的线性组合。 2.5 向量的外积两个向量的外积为一个矩阵: \\mathbf M= \\mathbf x \\mathbf y^t= \\left[ \\begin{matrix} x_1 \\\\ x_2 \\\\ \\vdots\\\\ x_d \\\\ \\end{matrix} \\right] \\left[ \\begin{matrix} y_1 & y_2 & \\dots & y_d\\\\ \\end{matrix} \\right]= \\left[ \\begin{matrix} x_1 y_1 & x_1 y_2 & \\dots & x_1 y_n\\\\ x_2 y_1 & x_2 y_2 & \\dots & x_2 y_n \\\\ \\vdots & \\vdots & \\ddots & \\vdots\\\\ x_d y_1 & x_d y_2 & \\dots & x_d y_n\\\\ \\end{matrix} \\right]\\tag{9} 3. 矩阵的导数3.1 取标量值的函数的导数设$f(\\mathbf x)$是一个取标量值得函数,有d个自变量$x_i(i=1,2, \\dots, d)$,用向量$\\mathbf x$表示。则函数$f(\\cdot)$关于自变量$\\mathbf x$的梯度为: \\nabla f(\\mathbf x)= \\frac{\\partial f(\\mathbf x)}{\\partial \\mathbf x}= \\left[ \\begin{matrix} \\frac{\\partial f(\\mathbf x)}{\\partial x_1} \\\\ \\frac{\\partial f(\\mathbf x)}{\\partial x_2} \\\\ \\vdots\\\\ \\frac{\\partial f(\\mathbf x)}{\\partial x_d} \\\\ \\end{matrix} \\right]\\tag{10}3.2 取值为向量的函数的导数设$\\mathbf f$是一个值为n维向量的向量函数,其自变量为d维向量$\\mathbf x$,则$\\mathbf f$关于自变量的梯度为: \\mathbf J(\\mathbf x)= \\frac{\\partial \\mathbf f(\\mathbf x)}{\\partial \\mathbf x}= \\left[ \\begin{matrix} \\frac{\\partial f_1(\\mathbf x)}{\\partial x_1} & \\dots & \\frac{\\partial f_1(\\mathbf x)}{\\partial x_d}\\\\ \\vdots & \\ddots & \\vdots \\\\ \\frac{\\partial f_n(\\mathbf x)}{\\partial x_1} & \\dots & \\frac{\\partial f_n(\\mathbf x)}{\\partial x_d} \\\\ \\end{matrix} \\right]\\tag{11}称矩阵$\\mathbf J(\\mathbf x)$为雅克比矩阵。 3.3 矩阵$\\mathbf M$关于标量参数$\\theta$的导数设矩阵$\\mathbf M$的每个元素都是关于某个标量参数$\\theta$的函数,则此矩阵关于参数的导数为: \\frac{\\partial \\mathbf M}{\\partial \\theta}= \\left[ \\begin{matrix} \\frac{\\partial m_{11}}{\\partial \\theta} & \\dots & \\frac{\\partial m_{1d}}{\\partial \\theta}\\\\ \\vdots & \\ddots & \\vdots \\\\ \\frac{\\partial m_{n1}}{\\partial \\theta} & \\dots & \\frac{\\partial m_{nd}}{\\partial \\theta} \\\\ \\end{matrix} \\right]\\tag{12}3.4 常用公式\\begin{alignat*}{1} \\frac{\\partial}{\\partial \\mathbf x}[\\mathbf M \\mathbf x]= \\mathbf M \\tag{13} \\\\ \\frac{\\partial}{\\partial \\mathbf x}[\\mathbf y^t \\mathbf x]= \\frac{\\partial}{\\partial \\mathbf x}[\\mathbf x^t \\mathbf y]= \\mathbf y \\tag{14}\\\\ \\frac{\\partial}{\\partial \\mathbf x}[\\mathbf x^t \\mathbf M \\mathbf x]= [\\mathbf M + \\mathbf M^t]\\mathbf x \\tag{15} \\end{alignat*} 4. 行列式和迹4.1 求解行列式 $2 \\times 2$的矩阵的行列式: |\\mathbf {M_{2 \\times 2}}|= m_{11}m_{22}-m_{21}m_{12}\\tag{16} 余子式:如果$\\mathbf M$为$d \\times d$的矩阵,任一元素$m_{ij}$的余子式$\\mathbf M_{i|j}$定义为,从原始矩阵$\\mathbf M$中分别划去第$i$行和第$j$列的所有元素后,得到的$(d-1) \\times (d-1)$的矩阵。$m_{ij}$的代数余子式$C_{ij}$为余子式$\\mathbf M_{i|j}$与$(-1)^{i+j}$的乘积。 以三维矩阵$\\mathbf A$为例: \\mathbf A=\\left[ \\begin{matrix} a_{11} & a_{12} & a_{13} \\\\ a_{21} & a_{22} & a_{23} \\\\ a_{31} & a_{32} & a_{33} \\\\ \\end{matrix} \\right]$a_{12}$的余子式$\\mathbf A_{1|2}$表示为: \\left[ \\begin{matrix} \\times & \\times & \\times \\\\ a_{21} & \\times & a_{23} \\\\ a_{31} & \\times & a_{33} \\\\ \\end{matrix} \\right] \\to \\mathbf A_{1|2}=\\left[ \\begin{matrix} a_{21} & a_{23} \\\\ a_{31} & a_{33} \\\\ \\end{matrix} \\right]$a_{12}$的代数余子式$C_{12}$表示为: C_{12}=(-1)^{1+2}|\\mathbf A_{1|2}|=-a_{21}a_{33}+a_{23}a_{31} 高阶矩阵的行列式:对于二阶矩阵的行列式,我们可以通过式(16)计算,对于更阶的矩阵,我们通过余子式来计算。如果$|\\mathbf M_{i|j}|$已经计算得到,那么高阶矩阵行列式的计算有如下公式: |\\mathbf M|=\\sum\\limits_{i=1}^d \\sum\\limits_{j=1}^d (-1)^{i+j} m_{ij} |\\mathbf M_{i|j}|\\tag{17} 行列式的一些性质:对于任意矩阵,有: |\\mathbf M|=|\\mathbf M^t|\\tag{18}当两个矩阵$\\mathbf M$与$\\mathbf N$的大小相同时,有: |\\mathbf M \\mathbf N|=|\\mathbf M| \\times |\\mathbf N|\\tag{19}矩阵的行列式在坐标系旋转时保持不变。 4.2 矩阵的迹 定义:对于$d \\times d$的方阵,矩阵的迹定义为主对角线上的元素之和,即: tr[\\mathbf M]= \\sum\\limits_{i=1}^d m_{ii}\\tag{20}矩阵的迹在坐标系旋转时保持不变。 5. 矩阵的逆 定义:只要一个$d \\times d$矩阵$\\mathbf M$的行列式不为零,那么它对应的逆矩阵$\\mathbf M^{-1}$就存在,并且满足: \\mathbf M \\mathbf M^{-1}= \\mathbf I\\tag{21} 伴随矩阵:矩阵$\\mathbf M$的伴随矩阵记为$Adj[\\mathbf M]$,它的第$i$行第$j$列元素为矩阵第$j$行第$i$列的余子式$C_{ji}$,即: Adj[\\mathbf M]= \\left[ \\begin{matrix} C_{11} & C_{21} & \\dots & C_{d1}\\\\ C_{12} & C_{22} & \\dots & C_{d2} \\\\ \\vdots & \\vdots & \\ddots & \\vdots\\\\ C_{1d} & C_{2d} & \\dots & C_{dd}\\\\ \\end{matrix} \\right]\\tag{22} 逆的计算: \\mathbf M^{-1}=\\frac{Adj[\\mathbf M]}{|\\mathbf M|}\\tag{23} 伪逆:如果$\\mathbf M$不是方阵,或者$\\mathbf M$的列向量不是线性无关的,则$\\mathbf M^{-1}$不存在,我们通常用伪逆矩阵$\\mathbf M^{\\dagger}$来代替$\\mathbf M^{-1}$。如果$\\mathbf M^t \\mathbf M$非奇异(即$|\\mathbf M^t \\mathbf M|\\neq0$),则伪逆矩阵定义为: \\mathbf M^{\\dagger}=[\\mathbf M^{t} \\mathbf M]^{-1} \\mathbf M^{t}\\tag{24} 逆的性质: [\\mathbf M \\mathbf N]^{-1}=\\mathbf M^{-1} \\mathbf N^{-1}\\tag{25} 6. 特征值与特征向量 线性方程组的解:对于$d \\times d$的矩阵$\\mathbf M$,一类非常重要的线性方程组的形式为: \\mathbf M \\mathbf x= \\lambda \\mathbf x\\tag{26}其中$\\lambda$为标量。上式可以写为: (\\mathbf M- \\lambda \\mathbf I) \\mathbf x=\\mathbf 0 \\tag{27}其中$\\mathbf I$为单位阵,为d维零向量。线性方程组(26)的解向量$\\mathbf x=\\mathbf e_i$和对应的标量系数$\\lambda = \\lambda_i$即为特征向量和特征值。一种求解特征向量和特征值得方法是求解特征方程: |\\mathbf M- \\lambda \\mathbf I|=0\\tag{28}特征方程的各个根就是特征值。然后对应每个根,我们通过求解线性方程组来得到特征值对应的特征向量。 重要性质:全部特征值之和为矩阵的迹,全部特征值的乘积为矩阵的行列式,即: \\begin{alignat*}{} tr[\\mathbf M]=\\sum\\limits_{i=1}^d \\lambda_i \\tag{29}\\\\ |\\mathbf M|= \\prod\\limits_{i=1}^d \\lambda_i\\tag{30} \\end{alignat*}{}","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"机器学习","slug":"机器学习","permalink":"http://yoursite.com/tags/机器学习/"},{"name":"数学","slug":"数学","permalink":"http://yoursite.com/tags/数学/"},{"name":"矩阵论","slug":"矩阵论","permalink":"http://yoursite.com/tags/矩阵论/"}]},{"title":"文献阅读记录","slug":"文献阅读记录","date":"2019-03-27T15:07:16.000Z","updated":"2019-04-01T04:20:52.000Z","comments":true,"path":"2019/03/27/文献阅读记录/","link":"","permalink":"http://yoursite.com/2019/03/27/文献阅读记录/","excerpt":"","text":"一、迁移学习相关文献1、迁移学习理论 Bendavid S , Blitzer J . Analysis of representations for domain adaptation[C]// International Conference on Neural Information Processing Systems. MIT Press, 2006.迁移学习第一篇理论文章。可以合理地认为,一个好的特征表示是有效进行域自适应的关键,本文的理论支持了这一论断。本文给出了一个泛化上界,这个上界是在源域训练的、应用在目标域的分类器的泛化上界。这个上界取决于特征表示,并明确地论证了低源域经验误差和小分布差别之间的折中。在标记函数$f^~$接近于假设空间$\\cal H$的假设下,我们可以从有限的样本中计算出这个边界。相关的分布散度项可以写成$\\cal A$距离。计算 $\\cal A$ 距离等价于寻找最小误差的分类器。 二、机器学习相关文献 三、深度学习理论相关文献","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"原创","slug":"原创","permalink":"http://yoursite.com/tags/原创/"},{"name":"文献","slug":"文献","permalink":"http://yoursite.com/tags/文献/"}]},{"title":"留言板","slug":"留言板","date":"2019-03-22T09:09:33.000Z","updated":"2019-03-22T09:26:54.000Z","comments":true,"path":"2019/03/22/留言板/","link":"","permalink":"http://yoursite.com/2019/03/22/留言板/","excerpt":"","text":"欢迎大家在此留言,不论是对本站的批评建议,还是对有些地方的批评指正,还是随便踩踩说说话,我都希望能看到大家的身影~","categories":[],"tags":[]},{"title":"github高星项目","slug":"github高星项目","date":"2019-03-11T06:56:45.000Z","updated":"2019-10-30T07:00:14.000Z","comments":true,"path":"2019/03/11/github高星项目/","link":"","permalink":"http://yoursite.com/2019/03/11/github高星项目/","excerpt":"","text":"更新日期:2019年10月30日 地址 描述 https://github.com/dragen1860/TensorFlow2.0Tutorials TensorFlow 2.0 Tutorials and Examples, CNN, RNN, GAN tutorials, etc. TF 2.0版入门实例代码,实战教程 https://github.com/ageron/tf2_course 用tf.keras和Tensorflow2.0做深度学习任务的jupyter教程 https://github.com/timsainb/tensorflow2-generative-models 基于tensorflow2.0的生成式模型合集 https://github.com/czy36mengfei/tensorflow2_tutorials_chinese tensorflow2.0中文教程 https://github.com/ageron/handson-ml2 《Hands-on Machine Learning with Scikit-Learn, Keras and TensorFlow》一书的例程 https://github.com/terryum/awesome-deep-learning-papers 这是来自于terryum整理的一份自2012年起被引用最多数量的深度学习论文列表 https://github.com/aikorea/awesome-rl 这是来自于aikorea整理的一份关于强化学习的代码、论文、应用、教程的清单 https://github.com/nightrome/really-awesome-gan 这是来自于nightrome整理的一份关于生成式对抗网络的代码、论文、应用、教程的清单 https://github.com/bulutyazilim/awesome-datascience 这是来自于bulutyazilim整理的一份关于数据科学在学习和应用实际问题的代码、论文、教程的清单 https://github.com/jbhuang0604/awesome-computer-vision 这是来自于jbhuang0604整理的一份关于计算机视觉的论文、教程、书籍、框架实现等的清单 - https://github.com/zzw922cn/awesome-speech-recognition-speech-synthesis-papers - https://github.com/edobashira/speech-language-processing 这是来自于jbhuang0604整理的自动语音识别、语音识别、语音合成、语言建模的论文、代码等资料 https://github.com/imhuay/Algorithm_Interview_Notes-Chinese 一个上万star的资料整理,2018/2019/校招/春招/秋招/算法/机器学习(Machine Learning)/深度学习(Deep Learning)/自然语言处理(NLP)/C/C++/Python/面试笔记 https://github.com/gopala-kr/meta-learning 元学习(meta-learning)相关文献资源大列表 https://github.com/hibayesian/awesome-automl-papers 自动机器学习(auto machine learning)相关资源列表 https://github.com/weiaicunzai/awesome-image-classification 图像分类作为计算机视觉的经典任务。一直被学者们研究探讨,本文介绍并比较了2014年以来较为出色的图像分类论文与代码 https://github.com/machinelearningmindset/deep-learning-ocean#what-s-the-point-of-this-open-source-project GitHub上的一份深度学习资源,涵盖深度学习的各个方面,包括论文、数据集、课程、图书、博客、教程、框架等 https://github.com/barebell/DA 本文整理了近几年期刊会议上领域自适应学习相关的论文与代码 https://github.com/wanghaisheng/awesome-ocr 超强合集:OCR文本检测干货汇总(含论文、源码、demo等资源) https://github.com/chichilicious/awesome-zero-shot-learning 深度学习工程师Shrisha Bharadwaj在Github罗列了Zero-Shot Learning相关资源大列表,主要是零样本物体识别系列,列出了近几年顶级会议上关于这一话题的相关论文,零样本学习系列还有其他话题 https://github.com/geekinglcq/CDCS 国内算法竞赛的各个优胜解决方案 https://github.com/datawhalechina/pumpkin-book 本项目对周志华老师的《机器学习》西瓜书里比较难理解的公式加以解析,以及对部分公式补充具体的推导细节 https://github.com/shunliz/Machine-Learning 机器学习原理的资源,前半部分关注数学基础,机器学习和深度学习的理论部分,详尽的公式推导,后半部分关注工程实践和理论应用部分 https://github.com/aikorea/awesome-rl 强化学习相关资源 https://github.com/awesomedata/awesome-public-datasets 汇总了非常多的公开数据集 https://github.com/kamyu104/LeetCode-Solutions Python / C++ 的LeetCode参考实现 https://github.com/D-X-Y/Awesome-NAS 神经网络结构搜索相关论文与代码","categories":[{"name":"机器学习文摘","slug":"机器学习文摘","permalink":"http://yoursite.com/categories/机器学习文摘/"}],"tags":[{"name":"github","slug":"github","permalink":"http://yoursite.com/tags/github/"},{"name":"项目","slug":"项目","permalink":"http://yoursite.com/tags/项目/"},{"name":"文摘","slug":"文摘","permalink":"http://yoursite.com/tags/文摘/"}]},{"title":"Python的30个技巧","slug":"Python的30个技巧","date":"2019-03-05T02:35:45.000Z","updated":"2020-08-03T07:24:54.000Z","comments":true,"path":"2019/03/05/Python的30个技巧/","link":"","permalink":"http://yoursite.com/2019/03/05/Python的30个技巧/","excerpt":"","text":"从公众号上看到了一篇文章《30个python编程技巧!》,觉得有些挺有用的,有的也一直在用,就挨个实现了一下。 1.原地交换两个数字x, y =10, 20 print(x, y) y, x = x, y print(x, y) 10 20 20 10 2.链状比较操作符n = 10 print(1 < n < 20) print(1 > n <= 9) True False 3.使用三元操作符来实现条件赋值[表达式为真的返回值] if [表达式] else [表达式为假的返回值] y = 20 x = 9 if (y == 10) else 8 print(x) 8 4.找abc中最小的数def small(a, b, c): return a if a<b and a<c else (b if b<a and b<c else c) print(small(1, 0, 1)) print(small(1, 2, 2)) print(small(2, 2, 3)) print(small(5, 4, 3)) 0133 5.列表推导x = [m**2 if m>10 else m**4 for m in range(50)] print(x) [0, 1, 16, 81, 256, 625, 1296, 2401, 4096, 6561, 10000, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484, 529, 576, 625, 676, 729, 784, 841, 900, 961, 1024, 1089, 1156, 1225, 1296, 1369, 1444, 1521, 1600, 1681, 1764, 1849, 1936, 2025, 2116, 2209, 2304, 2401] 4.多行字符串multistr = \"select * from multi_row \\ where row_id < 5\" print(multistr) select * from multi_row where row_id < 5 multistr = \"\"\"select * from multi_row where row_id < 5\"\"\" print(multistr) select * from multi_row where row_id < 5 multistr = (\"select * from multi_row\" \"where row_id < 5\" \"order by age\") print(multistr) select * from multi_rowwhere row_id < 5order by age 5.存储列表元素到新的变量testList = [1, 2, 3] x, y, z = testList # 变量个数应该和列表长度严格一致 print(x, y, z) 1 2 3 6.打印引入模块的绝对路径import threading import socket print(threading) print(socket) 7.交互环境下的“_”操作符在python控制台,不论我们测试一个表达式还是调用一个方法,结果都会分配给一个临时变量“_” 8.字典/集合推导testDic = {i: i * i for i in range(10)} testSet = {i * 2 for i in range(10)} print(testDic) print(testSet) {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81} {0, 2, 4, 6, 8, 10, 12, 14, 16, 18} 9.调试脚本用pdb模块设置断点 import pdb pdb.ste_trace() 10.开启文件分享python允许开启一个HTTP服务器从根目录共享文件 python -m http.server 11.检查python中的对象test = [1, 3, 5, 7] print(dir(test)) [‘add‘, ‘class‘, ‘contains‘, ‘delattr‘, ‘delitem‘, ‘dir‘, ‘doc‘, ‘eq‘, ‘format‘, ‘ge‘, ‘getattribute‘, ‘getitem‘, ‘gt‘, ‘hash‘, ‘iadd‘, ‘imul‘, ‘init‘, ‘iter‘, ‘le‘, ‘len‘, ‘lt‘, ‘mul‘, ‘ne‘, ‘new‘, ‘reduce‘, ‘reduce_ex‘, ‘repr‘, ‘reversed‘, ‘rmul‘, ‘setattr‘, ‘setitem‘, ‘sizeof‘, ‘str‘, ‘subclasshook‘, ‘append’, ‘clear’, ‘copy’, ‘count’, ‘extend’, ‘index’, ‘insert’, ‘pop’, ‘remove’, ‘reverse’, ‘sort’] test = range(10) print(dir(test)) [‘class‘, ‘contains‘, ‘delattr‘, ‘dir‘, ‘doc‘, ‘eq‘, ‘format‘, ‘ge‘, ‘getattribute‘, ‘getitem‘, ‘gt‘, ‘hash‘, ‘init‘, ‘iter‘, ‘le‘, ‘len‘, ‘lt‘, ‘ne‘, ‘new‘, ‘reduce‘, ‘reduce_ex‘, ‘repr‘, ‘reversed‘, ‘setattr‘, ‘sizeof‘, ‘str‘, ‘subclasshook‘, ‘count’, ‘index’, ‘start’, ‘step’, ‘stop’] 12.简化if语句# use following way to verify multi values if m in [1, 2, 3, 4]: # do not use following way if m==1 or m==2 or m==3 or m==4: 13.运行时检测python版本import sys if not hasattr(sys, \"hexversion\") or sys.version_info != (2, 7): print(\"sorry, you are not running on python 2.7\") print(\"current python version:\", sys.version) sorry, you are not running on python 2.7 current python version: 3.5.1 (v3.5.1:37a07cee5969, Dec 6 2015, 01:54:25) [MSC v.1900 64 bit (AMD64)] 14.组合多个字符串test = [\"I\", \"Like\", \"Python\"] print(test) print(\"\".join(test)) [‘I’, ‘Like’, ‘Python’] ILikePython 15.四种翻转字符串、列表的方式# 翻转列表本身 testList = [1, 3, 5] testList.reverse() print(testList) [5, 3, 1] # 在一个循环中翻转并迭代输出 for element in reversed([1, 3, 5]): print(element) 5 3 1 # 翻转字符串 print(\"Test Python\"[::-1]) nohtyP tseT # 用切片翻转列表 print([1, 3, 5][::-1]) [5, 3, 1] 16.用枚举在循环中找到索引test = [10, 20, 30] for i, value in enumerate(test): print(i, ':', value) 0 : 10 1 : 20 2 : 30 17.定义枚举量class shapes: circle, square, triangle, quadrangle = range(4) print(shapes.circle) print(shapes.square) print(shapes.triangle) print(shapes.quadrangle) 0 1 2 3 18.从方法中返回多个值def x(): return 1, 2, 3, 4 a, b, c, d = x() print(a, b, c, d) 1 2 3 4 19.使用*运算符unpack函数参数def test(x, y, z): print(x, y, z) testDic = {'x':1, 'y':2, 'z':3} testList = [10, 20, 30] test(*testDic) test(**testDic) test(*testList) z x y 1 2 3 10 20 30 20.用字典来存储表达式stdcalc = { \"sum\": lambda x, y: x + y, \"subtract\": lambda x, y: x - y } print(stdcalc[\"sum\"](9, 3)) print(stdcalc[\"subtract\"](9, 3)) 12 6 21.计算任何数的阶乘import functools result = (lambda k: functools.reduce(int.__mul__, range(1, k+1), 1))(3) print(result) 6 22.找到列表中出现次数最多的数test = [1, 2, 3, 4, 2, 2, 3, 1, 4, 4, 4, 4] print(max(set(test), key=test.count)) 4 23.重置递归限制python限制递归次数到1000,可以用下面方法重置 import sys x = 1200 print(sys.getrecursionlimit()) sys.setrecursionlimit(x) print(sys.getrecursionlimit()) 10001200 24.检查一个对象的内存使用import sys x = 1 print(sys.getsizeof(x)) # python3.5中一个32比特的整数占用28字节 28 25.使用slots减少内存开支import sys # 原始类 class FileSystem(object): def __init__(self, files, folders, devices): self.files = files self.folder = folders self.devices = devices print(sys.getsizeof(FileSystem)) # 减少内存后 class FileSystem(object): __slots__ = ['files', 'folders', 'devices'] def __init__(self, files, folders, devices): self.files = files self.folder = folders self.devices = devices print(sys.getsizeof(FileSystem)) 1016 888 26.用lambda 来模仿输出方法import sys lprint = lambda *args: sys.stdout.write(\" \".join(map(str, args))) lprint(\"python\", \"tips\", 1000, 1001) python tips 1000 1001 27.从两个相关序列构建一个字典t1 = (1, 2, 3) t2 = (10, 20, 30) print(dict(zip(t1, t2))) {1: 10, 2: 20, 3: 30} 28.搜索字符串的多个前后缀print(\"http://localhost:8888/notebooks/Untitled6.ipynb\".startswith((\"http://\", \"https://\"))) print(\"http://localhost:8888/notebooks/Untitled6.ipynb\".endswith((\".ipynb\", \".py\"))) True True 29.不使用循环构造一个列表import itertools import numpy as np test = [[-1, -2], [30, 40], [25, 35]] print(list(itertools.chain.from_iterable(test))) [-1, -2, 30, 40, 25, 35] 30.实现switch-case语句def xswitch(x): return xswitch._system_dict.get(x, None) xswitch._system_dict = {\"files\":10, \"folders\":5, \"devices\":2} print(xswitch(\"default\")) print(xswitch(\"devices\")) None 2","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"编程语言","slug":"编程语言","permalink":"http://yoursite.com/tags/编程语言/"},{"name":"python","slug":"python","permalink":"http://yoursite.com/tags/python/"},{"name":"原创","slug":"原创","permalink":"http://yoursite.com/tags/原创/"}]},{"title":"PHM专栏-文献汇总","slug":"PHM专栏-文献汇总","date":"2019-03-04T15:20:18.000Z","updated":"2020-08-03T02:56:16.000Z","comments":true,"path":"2019/03/04/PHM专栏-文献汇总/","link":"","permalink":"http://yoursite.com/2019/03/04/PHM专栏-文献汇总/","excerpt":"","text":"更新日期:2019年8月21日此页为PHM相关的文献汇总,主要关注机器学习在其中的应用 一、综述文献 文献 描述 日期 Hoang, Duy-Tang, and Hee-Jun Kang. “A survey on Deep Learning based bearing fault diagnosis.“ Neurocomputing (2018). 此文重点关注自编码器、限制性玻尔兹曼机、卷积神经网络在轴承故障诊断中的应用 2018.6 Liu R , Yang B , Zio E , et al. Artificial intelligence for fault diagnosis of rotating machinery: A review[J]. Mechanical Systems and Signal Processing, 2018, 108:33-47. 本文重点关注人工智能算法在旋转机械故障诊断中的应用,这些算法包括k近邻、朴素贝叶斯、支持向量机、人工神经网络、深度学习等 2018.2 Khan S , Yairi T . A review on the application of deep learning in system health management[J]. Mechanical Systems and Signal Processing, 2018, 107(1):241-265. 这篇文章系统地总结了人工智能在PHM中的应用,尤其是深度学习在这领域的应用。 2017.11 Rui Z , Ruqiang Y , Zhenghua C , et al. Deep learning and its applications to machine health monitoring[J]. Mechanical Systems and Signal Processing, 2019, 115:213-237. 重点介绍深度学习算法如自编码器、限制性玻尔兹曼机、深度信念网络、深度玻尔兹曼机、卷积神经网络和循环神经网络等在设备健康监测方面的应用 2016.12 Smith W A , Randall R B . Rolling element bearing diagnostics using the Case Western Reserve University data: A benchmark study[J]. Mechanical Systems and Signal Processing, 2015, 64-65:100-131. 本文使用三种方法对CWRU数据集做了研究与分析 2015.4 二、故障诊断1、卷积神经网络方法 文献 描述 日期 Zhu Z, Peng G, Chen Y, et al. A convolutional neural network based on a capsule network with strong generalization for bearing fault diagnosis[J]. Neurocomputing, 2019, 323: 62-75. 本文使用了胶囊网络结构,输入为二维频域图,最后有三个输出分支:分类故障类型、回归损坏程度和重构输入 2018.9 Liu H, Zhou J, Xu Y, et al. Unsupervised fault diagnosis of rolling bearings using a deep neural network based on generative adversarial networks[J]. Neurocomputing, 2018, 315: 412-424. 本文提出了Categorical Adversarial Autoencoder (CatAAE)结构,使用GAN的思想做无监督故障诊断 2018.8 Huang, Ruyi, et al. “Deep decoupling convolutional neural network for intelligent compound fault diagnosis.“ IEEE Access7 (2018): 1848-1858. 2018.7 Abdeljaber O , Avci O , Kiranyaz M S , et al. 1-D CNNs for Structural Damage Detection: Verification on a Structural Health Monitoring Benchmark Data[J]. Neurocomputing, 2017:S0925231217315886. 训练CNN需要大量的测量,尤其在CNN结构比较大的时候。本文提出一种基于CNN的加强的方法,只需测量两侧,不论结构多大。 2017.9 A convolutional neural network based feature learning and fault diagnosis method for the condition monitoring of gearbox 此文用频域信号作为输入,与原始信号、频域谱信号、时域-频域信号输入作对比。同时与从时域、频域、小波域提出的人工特征做对比。 2017.7 Zhang W , Li C , Peng G , et al. A deep convolutional neural network with new training methods for bearing fault diagnosis under noisy environment and different working load[J]. Mechanical Systems and Signal Processing, 2018, 100:439-453. 2017.6 Lu C, Wang Z, Zhou B. Intelligent fault diagnosis of rolling bearing using hierarchical convolutional network based health state classification[J]. Advanced Engineering Informatics, 2017, 32: 139-151. 构造特征输入矩阵,使用贪婪方法训练CNN 2017.2 Guo X, Chen L, Shen C. Hierarchical adaptive deep convolution neural network and its application to bearing fault diagnosis[J]. Measurement, 2016, 93: 490-502. 层次学习率自适应深度CNN(ADCNN),可以选择合适的学习率。层次结构:第一层次分类故障,第二层次估计故障大小 2016.7 Ince T, Kiranyaz S, Eren L, et al. Real-time motor fault detection by 1-D convolutional neural networks[J]. IEEE Transactions on Industrial Electronics, 2016, 63(11): 7067-7075. 使用原始信号作为CNN的输入 2016.5 Lee D, Siu V, Cruz R, et al. Convolutional neural net and bearing fault analysis[C]//Proceedings of the International Conference on Data Mining (DMIN). The Steering Committee of The World Congress in Computer Science, Computer Engineering and Applied Computing (WorldComp), 2016: 194. 使用原始信号作为CNN的输入 2016. Chen Z Q, Li C, Sanchez R V. Gearbox fault identification and classification with convolutional neural networks[J]. Shock and Vibration, 2015, 2015. 本文使用多种特征提出方式,将特征reshape成2D结构,输入到CNN中 2015.4 2、玻尔兹曼机、深度信念网络、多层感知机、自编码机、无监督方法 文献 描述 日期 Chen Z , Deng S , Chen X , et al. Deep neural networks-based rolling bearing fault diagnosis[J]. Microelectronics Reliability, 2017:S0026271417300513. 本文使用三种深度神经网络:深度玻尔兹曼机、深度信念网络、堆叠自编码机,来做轴承故障诊断 3、循环神经网络方法 文献 描述 Lei J , Liu C , Jiang D . Fault diagnosis of wind turbine based on Long Short-Term memory networks[J]. Renewable Energy, 2018. 本文提出了一种使用LSTM的端到端的故障诊断框架 4、迁移学习 文献 描述 日期 Wang Q , Michau G , Fink O . Domain Adaptive Transfer Learning for Fault Diagnosis[J]. 2019. 2019.5 Siyu S , Stephen M A , Ruqiang Y , et al. Highly-Accurate Machine Fault Diagnosis Using Deep Transfer Learning[J]. IEEE Transactions on Industrial Informatics, 2018:1-1. 2019.4 Yang, B., Lei, Y., Jia, F., & Xing, S. (2019). An intelligent fault diagnosis approach based on transfer learning from laboratory bearings to locomotive bearings. *Mechanical Systems and Signal Processing, 122*, 692-706. 2018.12 Guo, L., Lei, Y., Xing, S., Yan, T., & Li, N. (2018). Deep convolutional transfer learning network: A new method for intelligent fault diagnosis of machines with unlabeled data. IEEE Transactions on Industrial Electronics, 66(9), 7316-7325. 2018.9 Han T , Liu C , Yang W , et al. Deep Transfer Network with Joint Distribution Adaptation: A New Intelligent Fault Diagnosis Framework for Industry Application[J]. 2018. 2018.4 Cao P , Zhang S , Tang J . Pre-Processing-Free Gear Fault Diagnosis Using Small Datasets with Deep Convolutional Neural Network-Based Transfer Learning[J]. 2017. 2017.10 Wen L , Gao L , Li X . A New Deep Transfer Learning Based on Sparse Auto-Encoder for Fault Diagnosis[J]. IEEE Transactions on Systems, Man, and Cybernetics: Systems, 2017:1-9. 2017.9 Zhang B , Li W , Tong Z , et al. Bearing fault diagnosis under varying working condition based on domain adaptation[J]. 2017. 2017.7 Zhang R , Tao H , Wu L , et al. Transfer Learning with Neural Networks for Bearing Fault Diagnosis in Changing Working Conditions[J]. IEEE Access, 2017:1-1. 2017.6 Wei Z , Gaoliang P , Chuanhao L , et al. A New Deep Learning Model for Fault Diagnosis with Good Anti-Noise and Domain Adaptation Ability on Raw Vibration Signals[J]. Sensors, 2017, 17(2):425-. 本文提出一种名为Deep Convolutional Neural Networks with Wide First-layer Kernels(WDCNN)的结构,使用AdaBN来做领域自适应 2017.2 Lu W , Liang B , Cheng Y , et al. Deep Model Based Domain Adaptation for Fault Diagnosis[J]. IEEE Transactions on Industrial Electronics, 2016, PP(99):1-1. 2016.10 Wang J , Xie J , Zhang L , et al. A factor analysis based transfer learning method for gearbox diagnosis under various operating conditions[C]// 2016 International Symposium on Flexible Automation (ISFA). IEEE, 2016. 本文使用因子分析的方法寻找不同域之间的关键特征,通过将特征隐射到低维隐空间,选择出使域之间差别最小的特征,再将这些特征输入到分类器 2016.8 三、RUL预测1、卷积神经网络方法 文献 描述 Li X, Ding Q, Sun J Q. Remaining useful life estimation in prognostics using deep convolution neural networks[J]. Reliability Engineering & System Safety, 2018, 172: 1-11. 本文使用CNN做端到端的RUL预测。 2、循环神经网络方法 文献 描述 Ellefsen A L, Bjørlykhaug E, Æsøy V, et al. Remaining useful life predictions for turbofan engine degradation using semi-supervised deep architecture[J]. Reliability Engineering & System Safety, 2019, 183: 240-251. 本文使用玻尔兹曼机和LSTM两种网络结构,做半监督的RUL预测,同时使用遗传算法调参。 四、硕博论文 文献 描述 学位 石鑫. 基于深度学习的变压器故障诊断技术研究[D]. 2018. 使用限制波尔兹曼机与经 BP 微调之后的网络对分类能力进行研究,分析了隐层节点数、学习率与迭代次数对特征提取能力的影响;研究了基于 DBN 方法进行故障诊断的应用过程,对不同长度划分样本下的数据集的计算能力进行研究,通过参数寻优的方法改善手动调节参数没有依据的问题 硕士 江国乾. 基于排序模式分析与深度学习的风电设备故障诊断方法研究[D]. 2017. 以风电轴承为研究对象,使用排序模式分析方法提出排序信息散度指标,量化分析系统当前运行状态与健康参考状态间振动信号在高维相空间中排序模式概率分布的差异性。以风电齿轮箱对研究对象,使用基于堆叠去噪自编码器的频域故障特征自学习和诊断方法。针对风电齿轮箱时域振动信号的固有多尺度特性及传统卷积神经网络特征提取能力的不足,提出一种新的多尺度卷积神经网络结构。针对风电机组 SCADA 数据的非线性与多变量时空相关性,构建一种新的风电系统多变量故障检测模型:滑动窗去噪自编码器 博士","categories":[],"tags":[{"name":"PHM专栏","slug":"PHM专栏","permalink":"http://yoursite.com/tags/PHM专栏/"},{"name":"文献汇总","slug":"文献汇总","permalink":"http://yoursite.com/tags/文献汇总/"}]},{"title":"机器学习教程文摘","slug":"机器学习教程文摘","date":"2019-03-01T07:37:09.000Z","updated":"2019-03-12T15:12:50.000Z","comments":true,"path":"2019/03/01/机器学习教程文摘/","link":"","permalink":"http://yoursite.com/2019/03/01/机器学习教程文摘/","excerpt":"","text":"【更新日期】2019年3月12日 AlphaGo的制胜秘诀:蒙特卡洛树搜索初学者指南 20 道面试题助你拿下微软 AI offer kaggle出品的机器学习、python、数据科学课程 Ian Goodfellow发推讲2个机器学习黑魔法,教你如何推导公式 机器学习中,有哪些特征选择的工程方法? 如何选择损失函数 斯坦福大学Fall 2018课程-机器学习硬件加速器( 附PPT下载) UCL等三强联手提出完全可微自适应神经树:神经网络与决策树完美结合 通俗理解kaggle比赛大杀器xgboost XGBoost参数调优完全指南(附Python代码) 用简单易懂的例子解释隐马尔可夫模型 机器学习模型该如何选择和评估?希望这个小册子能帮到你! 想要算一算Wasserstein距离?这里有一份PyTorch实战 最全的机器学习可解释性资料(machine-learning-interpretability)","categories":[{"name":"机器学习文摘","slug":"机器学习文摘","permalink":"http://yoursite.com/categories/机器学习文摘/"}],"tags":[{"name":"文摘","slug":"文摘","permalink":"http://yoursite.com/tags/文摘/"},{"name":"机器学习","slug":"机器学习","permalink":"http://yoursite.com/tags/机器学习/"}]},{"title":"深度学习理论文摘","slug":"深度学习理论文摘","date":"2019-03-01T07:33:29.000Z","updated":"2019-03-14T14:48:06.000Z","comments":true,"path":"2019/03/01/深度学习理论文摘/","link":"","permalink":"http://yoursite.com/2019/03/01/深度学习理论文摘/","excerpt":"","text":"【更新日期】2019年3月14日 新型RNN:将层内神经元相互独立以提高长程记忆 | CVPR 2018论文解读 再谈变分自编码器VAE:从贝叶斯观点出发 基于GAN的字体风格迁移 | CVPR 2018论文解读 一文读懂深度学习模型近年来重要进展(附梳理图) 学习如何学习的算法:简述元学习研究方向现状 【教程】可视化CapsNet,详解Hinton等人提出的胶囊概念与原理 变分自编码器VAE:这样做为什么能成? 深度学习DenseNet算法详解 学习如何学习的算法:简述元学习研究方向现状 一文简述ResNet及其多种变体 深度卷积神经网络演化历史及结构改进脉络-40页长文全面解读 深度学习在CTR预估中的应用 | CTR深度模型大盘点 CNN可视化最新研究方法进展(附结构、算法) BP神经网络算法推导及代码实现笔记 【一个神经元统治一切】ResNet 强大的理论证明 FAGAN:完全注意力机制(Full Attention)GAN,Self-attention+GAN 模型不收敛,训练速度慢,如何才能改善 GAN 的性能? 深度概率生成模型—156页普林斯顿教程带你回顾深度生成模型最新发展脉络 CMU大学76页深度学习课程:变分自编码器(VAE, Variational Autoencoder) 新手必看:生成对抗网络的初学者入门指导 WGAN-div:默默无闻的WGAN填坑者 | 附开源代码 Meta-Learning 元学习:学会快速学习 从Wasserstein距离、对偶理论到WGAN 中国学霸本科生提出AI新算法:速度比肩Adam,性能媲美SGD,ICLR领域主席赞不绝口 Google联合OpenAI揭秘神经网络黑箱:AI的智慧,都藏在「激活地图」里","categories":[{"name":"机器学习文摘","slug":"机器学习文摘","permalink":"http://yoursite.com/categories/机器学习文摘/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"http://yoursite.com/tags/深度学习/"},{"name":"文摘","slug":"文摘","permalink":"http://yoursite.com/tags/文摘/"},{"name":"理论","slug":"理论","permalink":"http://yoursite.com/tags/理论/"}]},{"title":"会议-论文","slug":"会议-论文","date":"2019-03-01T07:21:33.000Z","updated":"2019-05-10T07:19:24.000Z","comments":true,"path":"2019/03/01/会议-论文/","link":"","permalink":"http://yoursite.com/2019/03/01/会议-论文/","excerpt":"","text":"【更新日期】2019年5月10日 发布论文“可解释性的基石” 使用可进化的AutoML发现神经网络架构 【ICLR 2018】Google 研究盘点,76篇论文抢先看 如何实现少样本学习?先让神经网络get√视觉比较能力 综述:图像风格化算法最全盘点 | 内附大量扩展应用 CVPR 2018:阿里提出新零样本学习方法,有效解决偏置问题 图深度学习(GraphDL),下一个人工智能算法热点?一文了解最新GDL相关文章 【CNN已老,GNN来了】DeepMind、谷歌大脑、MIT等27位作者重磅论文,图网络让深度学习也能因果推理 小样本学习年度进展|VALSE2018 【ICML 2018最佳论文出炉】MIT、UC伯克利获最佳论文,复旦大学等获提名奖 ICML 2018 | 训练可解释、可压缩、高准确率的LSTM ICLR 2018 Wasserstein自编码器 DeepMind无监督表示学习重大突破:语音、图像、文本、强化学习全能冠军! 2019人工智能学术会议列表,投稿同学看过来! 谷歌大作:自动改良反向传播算法,训练速度再提升! 新智元 1周前 悉尼大学欧阳万里等人30页最新目标检测综述 NIPS 2018 | MIT新研究参透批归一化原理 烧脑!CMU、北大等合著论文真的找到了神经网络的全局最优解 NIPS 2018 | Quoc Le提出卷积网络专属正则化方法DropBlock 干货|36页最新深度学习综述论文:算法、技术、应用,181篇参考文献 ICLR 2019 | 用浅度学习训练深度网络:避开端到端深度学习难题 106页《深度CNN-目标检测》综述进展论文 深度学习之路——论文阅读 【干货|如何打开黑盒子模型?】41页最新机器学习可解释模型综述论文 Dropout可能要换了,Hinton等研究者提出神似剪枝的Targeted Dropout 综述:DenseNet—Dense卷积网络(图像分类) 专栏 | MSRA视觉计算组提出第二代可变形卷积网络,增强形变,更好效果 「如何跳出鞍点?」NeurIPS 2018优化相关论文提前看 NeurIPS 2018 | BP不用算梯度,这样的线性反向传播也能Work! Facebook AI实验室何恺明等人提出视频识别SlowFast网络 GAN 2.0!英伟达“风格迁移”面部生成器,世间万物逼真呈现 16篇论文、70多页PPT帮你优化深度学习模型,免费下载 | 资源 自编码表示学习 25页最新进展综述,90篇参考文献 硬核NeruIPS 2018最佳论文,一个神经了的常微分方程 CNN已老,GNN来了!清华大学孙茂松组一文综述GNN 元学习究竟是什么?这《基于梯度的元学习》199页伯克利博士论文带你回顾元学习最新发展脉络 降维打击:这款GAN可以让真人「二次元化」 学界 | 一览 CMU 的 33 篇 ICML 2019 论文 学界 | 向频域方向演进的卷积网络:OctConv用更低计算力做到更高准确率 元学习(Meta-Learning) 综述及五篇顶会论文推荐 何恺明等人新作:效果超ResNet,利用NAS方法设计随机连接网络 深度学习理论与架构最新进展综述论文,66页pdf,333篇参考文献","categories":[{"name":"机器学习文摘","slug":"机器学习文摘","permalink":"http://yoursite.com/categories/机器学习文摘/"}],"tags":[{"name":"文摘","slug":"文摘","permalink":"http://yoursite.com/tags/文摘/"},{"name":"会议","slug":"会议","permalink":"http://yoursite.com/tags/会议/"},{"name":"论文","slug":"论文","permalink":"http://yoursite.com/tags/论文/"}]},{"title":"框架-代码-实践","slug":"框架-代码-实践","date":"2019-03-01T07:03:46.000Z","updated":"2019-05-10T07:17:34.000Z","comments":true,"path":"2019/03/01/框架-代码-实践/","link":"","permalink":"http://yoursite.com/2019/03/01/框架-代码-实践/","excerpt":"","text":"【更新日期】2019年5月10日 教你用 Python 和 Keras 建立自己的 AlphaZero 如何利用深度学习写诗歌 手把手:用Python实现一个基于RSA算法的区块链客户端 python语音识别终极指南 用 TensorFlow 让机器人唱首歌给你听 用机器学习搞艺术,谷歌 Megenta 项目集锦(附 Github) 这些资源你肯定需要!超全的GAN PyTorch+Keras实现集合 基于Keras的注意力机制实战 AI 开发者看过来,主流移动端深度学习框架大盘点 【代码+教程】重现“世界模型”实验,无监督方式快速训练 高效CNN推理库、多款AlphaGo实现…你们喜欢的Github项目精选又来了! 吴恩达授课,斯坦福CS230深度学习课程资源开放 10行代码实现目标检测,请收下这份教程 Github上一些精致且实用的TensorFlow项目及相关论文 貌离神合的RNN与ODE:花式RNN简介 手把手教你Python实现自动贝叶斯调整超参数 想化身AI领域艺术家?使用 tf.keras 和 Eager Execution吧 使用估算器、tf.keras 和 tf.data 进行多 GPU 训练 深度学习TensorFlow实现集合 简单粗暴 TensorFlow Eager 教程 【GAN货】用神经网络生成音乐 谷歌开源集成学习工具AdaNet:2017年提出的算法终于实现了 以为GAN只能“炮制假图”?它还有这7种另类用途 【干货】用神经网络识别歌曲流派(附代码) 博客 | Github开源人体姿态识别项目OpenPose中文文档 一个模型搞定所有风格转换,直接在浏览器实现(demo+代码) AI 入门 | “君子动口”,用语音控制智能家居 玩转TensorFlow?你需要知道这30个功能 TensorFlow 指南:GPU 的使用 TensorFlow 不仅用于机器学习,还能模拟偏微分方程 TensorFlow手把手教你概率编程:TF Probability内置了开源教材,新手友好 Pytoch1.0深度学习如何玩?这一门含900页ppt和代码实例的深度学习课程带你飞 简单的音频识别 工具 | Facebook 发布无梯度优化开源工具 Nevergrad,可应用于各类机器学习问题 快速上手 — TensorFlow Probability 内置概率编程教材 秒变蒙娜丽莎毫无PS痕迹!AI神器已开源,嵌入任何名画 AutoGraph:图的简易控制流程 NYU、AWS联合推出:全新图神经网络框架DGL正式发布 将深度学习模型部署为web应用有多难?答案自己找 Keras作者:TF 2.0+Keras深度学习研究你需要了解的12件事 超强干货!TensorFlow易用代码大集合,请尽情复制粘贴 GitHub 项目推荐 | 用深度学习让你的照片变得美丽 在TensorFlow+Keras环境下使用RoI池化一步步实现注意力机制 AutoML:机器学习的下一波浪潮(附代码&链接) 使用 TensorFlow Probability Layers 的变分自编码器","categories":[{"name":"机器学习文摘","slug":"机器学习文摘","permalink":"http://yoursite.com/categories/机器学习文摘/"}],"tags":[{"name":"文摘","slug":"文摘","permalink":"http://yoursite.com/tags/文摘/"},{"name":"框架","slug":"框架","permalink":"http://yoursite.com/tags/框架/"},{"name":"代码","slug":"代码","permalink":"http://yoursite.com/tags/代码/"},{"name":"实践","slug":"实践","permalink":"http://yoursite.com/tags/实践/"}]},{"title":"文档-ppt-教程-其他","slug":"文档-ppt-教程-其他","date":"2019-03-01T06:49:20.000Z","updated":"2019-03-01T07:02:38.000Z","comments":true,"path":"2019/03/01/文档-ppt-教程-其他/","link":"","permalink":"http://yoursite.com/2019/03/01/文档-ppt-教程-其他/","excerpt":"","text":"【更新日期】2019年1月1日 机器学习:3个知乎大神回答、5个新人常见问题和3个学习规划 揭秘美团点评背后的AI超级大脑,看这14篇文章就够了 微软AI面试题有多难?这里有一份样卷 可微编程:打开深度学习的黑盒子 干货!从基础到进阶,长文解析微软量子计算概念和算法(上) 干货!从基础到进阶,长文解析微软量子计算概念和算法(下) 剑桥大学:156页PPT全景展示AI过去的12个月(附下载) 【2018 AI全景报告】全球AI人才供需分布图,可用AI专才仅3000 如何搞明白深度学习的算法、理论与计算系统?这一份CMU邢波教授286页全面为你阐述 隐式自编码器(Implicit Autoencoders),自编码器新方法 数据少,就享受不到深度学习的红利了么?总是有办法的! 150页Slides深度学习革命:基础,优化,正则——CMU&斯坦福JSM 2018教程 深度学习贝叶斯,这是一份密集的6天速成课程(视频与PPT) 【ECCV2018教程】220页深度神经网络训练归一化: 数学基础与理论、挑战(附pdf下载) 如何撰写一篇优秀的研究论文?这一份68页PPT告诉你 【GAN货】生成式对抗网络资料荟萃(原理/教程/报告/论文/实战/资料库) 帝国理工学院134页机器学习中的数学知识(附下载) 246页《统计机器学习与凸优化》教程PPT下载 一文读懂机器学习需要哪些数学知识 AI算法工程师手册 【教程】语音识别中的End-to-End模型教程(附178页PDF全文下载) DeepMind高赞课程:24小时看完深度强化学习最新进展(视频) 计算机视觉入门教程系列—125页带你回顾CV发展脉络 吴恩达CS230深度学习课小抄来了,图多字少还免费下载 | 斯坦福出品 斯坦福CS230课程简明讲义,CNN、RNN包教包会 【NeurIPS2018】无监督深度学习全景教程(附193页PDF下载) NeruIPS 2018 所有tutorial的视频与PPT汇总【附下载】 伦敦帝国理工学院 134页《深度学习数学基础笔记》(附下载) Yann Lecun自监督学习指南(附114页Slides全文下载) 这一份217页斯坦福大学统计学习理论笔记,Percy Liang带你搞清楚难懂的理论基础 921页《用Python3带你从小白入门机器学习实战》教程手册 这是一本好玩的可视化统计概率入门书,66页pdf下载 史上最详细的自动驾驶汽车技术介绍【硬件+软件】","categories":[{"name":"机器学习文摘","slug":"机器学习文摘","permalink":"http://yoursite.com/categories/机器学习文摘/"}],"tags":[{"name":"文摘","slug":"文摘","permalink":"http://yoursite.com/tags/文摘/"},{"name":"文档","slug":"文档","permalink":"http://yoursite.com/tags/文档/"},{"name":"ppt","slug":"ppt","permalink":"http://yoursite.com/tags/ppt/"},{"name":"教程","slug":"教程","permalink":"http://yoursite.com/tags/教程/"}]},{"title":"竞赛类","slug":"竞赛类","date":"2019-03-01T03:45:41.000Z","updated":"2019-05-10T07:08:20.000Z","comments":true,"path":"2019/03/01/竞赛类/","link":"","permalink":"http://yoursite.com/2019/03/01/竞赛类/","excerpt":"","text":"【更新日期】2019年5月10日 Kaggle Competition Past Solutions Data Science Glossary on Kaggle ! 如何从数据挖掘比赛中脱颖而出?快来 get 阿里妈妈广告算法赛亚军套路吧!(内附代码和数据链接) 从0上手Kaggle图像分类挑战:冠军解决方案详解 我赢得 Kaggle 竞赛的第五名,这些经验分享给你 应对时间序列问题有何妙招(Kaggle比赛亚军) 干货 | 各大数据竞赛 Top 解决方案汇总 NeurIPS 2018 AutoML Phase1 冠军队伍 DeepSmart 团队解决方案分享 司法鉴定牵手深度学习:Kaggle 相机型号识别大赛深度分析 Kaggle数据挖掘比赛经验分享,陈成龙博士整理","categories":[{"name":"机器学习文摘","slug":"机器学习文摘","permalink":"http://yoursite.com/categories/机器学习文摘/"}],"tags":[{"name":"文摘","slug":"文摘","permalink":"http://yoursite.com/tags/文摘/"},{"name":"机器学习","slug":"机器学习","permalink":"http://yoursite.com/tags/机器学习/"},{"name":"竞赛","slug":"竞赛","permalink":"http://yoursite.com/tags/竞赛/"}]},{"title":"编程语言/算法","slug":"编程语言-算法","date":"2019-03-01T03:42:53.000Z","updated":"2019-03-14T14:38:12.000Z","comments":true,"path":"2019/03/01/编程语言-算法/","link":"","permalink":"http://yoursite.com/2019/03/01/编程语言-算法/","excerpt":"","text":"【更新日期】2019年3月14日 快来操纵你的 GPU:CUDA 编程入门极简教程 70个NumPy练习:在Python下一举搞定机器学习矩阵运算 真爱 | 算法虐我千百遍,我待算法如初恋 算法和编程面试题精选TOP50!(附代码+解题思路+答案) 这个Python资源在GitHub上标星超8000,现在被翻译成了中文 | 资源 每个程序员都应该收藏的算法复杂度速查表 干货 | Github项目推荐 : GANSynth: 用GANs创作音乐","categories":[{"name":"机器学习文摘","slug":"机器学习文摘","permalink":"http://yoursite.com/categories/机器学习文摘/"}],"tags":[{"name":"编程语言","slug":"编程语言","permalink":"http://yoursite.com/tags/编程语言/"},{"name":"文摘","slug":"文摘","permalink":"http://yoursite.com/tags/文摘/"},{"name":"算法","slug":"算法","permalink":"http://yoursite.com/tags/算法/"},{"name":"机器学习","slug":"机器学习","permalink":"http://yoursite.com/tags/机器学习/"}]},{"title":"迁移学习","slug":"迁移学习","date":"2019-03-01T03:09:22.000Z","updated":"2019-03-01T03:18:50.000Z","comments":true,"path":"2019/03/01/迁移学习/","link":"","permalink":"http://yoursite.com/2019/03/01/迁移学习/","excerpt":"","text":"【更新日期】2018年11月17日 Github 项目推荐 | 不容错过的迁移学习领域自适应资源 基于深度迁移学习进行时间序列分类 2017年领域自适应发展回顾","categories":[{"name":"机器学习文摘","slug":"机器学习文摘","permalink":"http://yoursite.com/categories/机器学习文摘/"}],"tags":[{"name":"文摘","slug":"文摘","permalink":"http://yoursite.com/tags/文摘/"},{"name":"机器学习","slug":"机器学习","permalink":"http://yoursite.com/tags/机器学习/"},{"name":"迁移学习","slug":"迁移学习","permalink":"http://yoursite.com/tags/迁移学习/"}]},{"title":"计算机视觉","slug":"计算机视觉","date":"2019-03-01T03:04:26.000Z","updated":"2019-03-01T03:14:24.000Z","comments":true,"path":"2019/03/01/计算机视觉/","link":"","permalink":"http://yoursite.com/2019/03/01/计算机视觉/","excerpt":"","text":"【更新日期】2018年11月17日 如何走近深度学习人脸识别?你需要这篇超长综述 | 附开源代码 终于!Supervise.ly 发布人像分割数据集啦(免费开源) 【干货】计算机视觉实战系列07——用Python做图像处理 详解计算机视觉五大技术:图像分类、对象检测、目标跟踪、语义分割和实例分割 基于深度学习的目标检测算法综述 深度学习之目标检测网络学习总结(from RCNN to YOLO V3) DL目标检测技术演进(通俗易懂,看一遍能记一辈子!) 从R-CNN到YOLO,一文带你了解目标检测模型(附论文下载) CNN模型的可视化 从YOLOv1到YOLOv3,目标检测的进化之路 资源 | 深度学习图像标注工具汇总 博客 | 基于深度学习的目标检测算法综述(一) 博客 | 基于深度学习的目标检测算法综述(二) 一图概览整个CV的核心知识体系 资源 | 1460万个目标检测边界框:谷歌开源Open Images V4数据集 南开大学提出最新边缘检测与图像分割算法,精度刷新记录(附开源地址) 一文带你读懂 OCR","categories":[{"name":"机器学习文摘","slug":"机器学习文摘","permalink":"http://yoursite.com/categories/机器学习文摘/"}],"tags":[{"name":"计算机视觉","slug":"计算机视觉","permalink":"http://yoursite.com/tags/计算机视觉/"},{"name":"机器学习, 文摘","slug":"机器学习,-文摘","permalink":"http://yoursite.com/tags/机器学习,-文摘/"}]},{"title":"既要担起生活的责任,又要对得起梦想","slug":"既要担起生活的责任,又要对得起梦想","date":"2019-03-01T03:01:56.000Z","updated":"2019-03-01T03:04:12.000Z","comments":true,"path":"2019/03/01/既要担起生活的责任,又要对得起梦想/","link":"","permalink":"http://yoursite.com/2019/03/01/既要担起生活的责任,又要对得起梦想/","excerpt":"","text":"说到李飞飞,我想在AI领域应该是无人不知无人不晓。这位杰出的华人女科学家,推动了世界人工智能的发展,如果没有她,AI可能还处在低谷中。她36岁成为斯坦福大学终身教授,担任斯坦福人工智能实验室和视觉实验室主任;她领导建立了ImageNet数据集,其比赛诞生了AlexNet、ResNet、Inception等具有深远影响的技术;她担任了谷歌云人工智能团队主管,领导了谷歌AI中国中心。 在去年的中国计算机大会上,我有幸见到了李飞飞,并得到了她的签名。在会场休息期间,她被众人围住签名、合影,在她周围的人比丘成桐的周围还要多。这次要说的不是她的科研成果。前段时间李飞飞从谷歌离职,重回斯坦福大学主管人工智能实验室,看到的一篇文章令我有所感触。文章中有段李飞飞的采访: 人生最大的挑战,其实是不辜负你最大的潜能,又不辜负你身上的责任,以及诚实面对你自己内心所希望追求的事业。 李飞飞的经历也是比较传奇了,她出生在北京,16岁移民到美国。迫于生计,李飞飞在上学同时在中餐馆、洗衣店打零工,曾对英文一窍不通。在她的努力下,她被普林斯顿大学计算机系录取。在大学毕业后她做了一件不可思议的事情:去西藏研究了一年藏药。 我非常看重具体科研项目在更大领域范围内的意义,每一项研究看是之前都要经过深思熟虑。虽然有些东西看起来没有用,比如我去研究藏药,但只要是深思熟虑过的、认准的,之后肯定会起到作用的,别心急。 从西藏回来后,她继续读博。此时家庭仍然不富裕,但她还是坚持学术研究造福世界的梦想,拿到了加州理工学院的博士学位,方向是人工智能和计算神经科学。在高校工作时,她选择了专攻当时非常冷门的计算机图像识别技术。为了训练计算机识图,2007年起她开始建立ImageNet数据集,包含了1400万张图片,2w多个类别。2009年,她获得了斯坦福大学的终身教授。 学术界和工业界人才的互动和思想的交流,一直是硅谷传奇的重要精髓。人类前行的道路需要思想灯塔的照耀,这是学术界和思想界,在这个重要的历史时刻义不容辞的历史责任和机会。只有以人为本的科技才能真正地造福人类。 生活也许是艰难的,在承担生活这一重责时,也希望我们能抬头看看星空,看看我们当时曾向往的星空。也许不是为了什么崇高的理想,也许不是为了改变世界,也许只是为了纯粹想做一件事而已。希望我们在脚踏实地的时候,也能看一眼头顶上的星空,不要忘了她在你心中曾是多么的璀璨。 既要担起生活的责任,又要对得起梦想。祝福李飞飞,也祝福每一个人,不辜负我们的美好时光。","categories":[{"name":"随笔","slug":"随笔","permalink":"http://yoursite.com/categories/随笔/"}],"tags":[{"name":"随笔","slug":"随笔","permalink":"http://yoursite.com/tags/随笔/"},{"name":"梦想","slug":"梦想","permalink":"http://yoursite.com/tags/梦想/"}]},{"title":"深度学习调参经验汇总","slug":"深度学习调参经验汇总","date":"2019-03-01T02:41:51.000Z","updated":"2019-08-16T10:11:52.000Z","comments":true,"path":"2019/03/01/深度学习调参经验汇总/","link":"","permalink":"http://yoursite.com/2019/03/01/深度学习调参经验汇总/","excerpt":"","text":"此篇文章是在原创教程这个栏目下,但实际上是一篇汇总整理文章。相信大家在做深度学习时对调参尤为无奈,经验不足乱调一通,或者参数太多无从下手,我也如此。希望通过此文汇总网上一些调参的经验方法,供大家参考。此文会对网上每一篇调参文章做简练的总结与提炼,以此为此文的组成单元,并附上原文的链接。如果遇到不对的地方,欢迎指正~本文也将不定期更新,最后祝大家调参(炼金)顺利! 有多少人工,就有多少智能!(笑哭脸) 正文开始 UNIT 1 case1:网络错误没有正确训练,损失完全不收敛。可能两种原因:1,错误的input data,网络无法学习。 2,错误的网络,网络无法学习。解决办法:(1)请检测自己的数据是否存在可以学习的信息,这个数据集中的数值是否泛化(防止过大或过小的数值破坏学习)。(2)如果是错误的数据则你需要去再次获得正确的数据,如果是数据的数值异常我们可以使用zscore函数来解决这个问题(3)如果是网络的错误,则希望调整网络,包括:网络深度,非线性程度,分类器的种类等等。 case2:部分收敛。可能原因:1.underfitting,就是网络的分类太简单了没办法去分类,因为没办法分类就是没办法学到正确的知识。2.overfitting,就是网络的分类太复杂了以至于它可以学习数据中的每一个信息甚至是错误的信息他都可以学习。解决办法:(1)underfitting: 增加网络的复杂度(深度),降低learning rate,优化数据集,增加网络的非线性度(ReLu),采用batch normalization。(2)overfitting: 丰富数据,增加网络的稀疏度,降低网络的复杂度(深度),L1 regularization,L2 regulariztion,添加Dropout,Early stopping,适当降低Learning rate,适当减少epoch的次数, case3:全部收敛但效果不好。这是个好的开始,接下来我们要做的就是微调一些参数。解决办法:调整方法就是保持其他参数不变,只调整一个参数。这里需要调整的参数会有:learning rate,minibatch size,epoch,filter size,number of filter 原文链接:https://blog.csdn.net/qq\\_20259459/article/details/70316511 UNIT 2 好的实验环境是成功的一半:(1)将各个参数的设置部分集中在一起。如果参数的设置分布在代码的各个地方,那么修改的过程想必会非常痛苦。(2)可以输出模型的损失函数值以及训练集和验证集上的准确率。(3)可以考虑设计一个子程序,可以根据给定的参数,启动训练并监控和周期性保存评估结果。再由一个主程序,分配参数以及并行启动一系列子程序。 画图:画图是一个很好的习惯,一般是训练数据遍历一轮以后,就输出一下训练集和验证集准确率。同时画到一张图上。这样训练一段时间以后,如果模型一直没有收敛,那么就可以停止训练,尝试其他参数了,以节省时间。 如果训练到最后,训练集,测试集准确率都很低,那么说明模型有可能欠拟合。那么后续调节参数方向,就是增强模型的拟合能力。例如增加网络层数,增加节点数,减少dropout值,减少L2正则值等等。 如果训练集准确率较高,测试集准确率比较低,那么模型有可能过拟合,这个时候就需要向提高模型泛化能力的方向,调节参数。 从粗到细分阶段调参:(1)建议先参考相关论文,以论文中给出的参数作为初始参数。至少论文中的参数,是个不差的结果。(2)如果找不到参考,那么只能自己尝试了。可以先从比较重要,对实验结果影响比较大的参数开始,同时固定其他参数,得到一个差不多的结果以后,在这个结果的基础上,再调其他参数。例如学习率一般就比正则值,dropout值重要的话,学习率设置的不合适,不仅结果可能变差,模型甚至会无法收敛。(3)如果实在找不到一组参数,可以让模型收敛。那么就需要检查,是不是其他地方出了问题,例如模型实现,数据等等。 提高速度:调参只是为了寻找合适的参数,而不是产出最终模型。一般在小数据集上合适的参数,在大数据集上效果也不会太差。因此可以尝试对数据进行精简,以提高速度,在有限的时间内可以尝试更多参数。(1)对训练数据进行采样。例如原来100W条数据,先采样成1W,进行实验看看。(2)减少训练类别。例如手写数字识别任务,原来是10个类别,那么我们可以先在2个类别上训练,看看结果如何。 超参数范围:建议优先在对数尺度上进行超参数搜索。比较典型的是学习率和正则化项,我们可以从诸如0.001 0.01 0.1 1 10,以10为阶数进行尝试。因为他们对训练的影响是相乘的效果。不过有些参数,还是建议在原始尺度上进行搜索,例如dropout值: 0.3 0.5 0.7)。 经验参数: learning rate: 1 0.1 0.01 0.001, 一般从1开始尝试。很少见learning rate大于10的。学习率一般要随着训练进行衰减。衰减系数一般是0.5。 衰减时机,可以是验证集准确率不再上升时,或固定训练多少个周期以后。 不过更建议使用自适应梯度的办法,例如adam,adadelta,rmsprop等,这些一般使用相关论文提供的默认值即可,可以避免再费劲调节学习率。对RNN来说,有个经验,如果RNN要处理的序列比较长,或者RNN层数比较多,那么learning rate一般小一些比较好,否则有可能出现结果不收敛,甚至Nan等问题。 网络层数: 先从1层开始。 每层结点数: 16 32 128,超过1000的情况比较少见。超过1W的从来没有见过。 batch size: 128上下开始。batch size值增加,的确能提高训练速度。但是有可能收敛结果变差。如果显存大小允许,可以考虑从一个比较大的值开始尝试。因为batch size太大,一般不会对结果有太大的影响,而batch size太小的话,结果有可能很差。 clip c(梯度裁剪): 限制最大梯度,其实是value = sqrt(w1^2+ w2^2….),如果value超过了阈值,就算一个衰减系系数,让value的值等于阈值: 5,10,15 dropout: 0.5 L2正则:1.0,超过10的很少见。 词向量embedding大小:128,256 正负样本比例: 这个是非常忽视,但是在很多分类问题上,又非常重要的参数。很多人往往习惯使用训练数据中默认的正负类别比例,当训练数据非常不平衡的时候,模型很有可能会偏向数目较大的类别,从而影响最终训练结果。除了尝试训练数据默认的正负类别比例之外,建议对数目较小的样本做过采样,例如进行复制。提高他们的比例,看看效果如何,这个对多分类问题同样适用。 在使用mini-batch方法进行训练的时候,尽量让一个batch内,各类别的比例平衡,这个在图像识别等多分类任务上非常重要。 自动调参: Gird Search. 这个是最常见的。具体说,就是每种参数确定好几个要尝试的值,然后像一个网格一样,把所有参数值的组合遍历一下。优点是实现简单暴力,如果能全部遍历的话,结果比较可靠。缺点是太费时间了,特别像神经网络,一般尝试不了太多的参数组合。 Random Search。Bengio在Random Search for Hyper-Parameter Optimization中指出,Random Search比Gird Search更有效。实际操作的时候,一般也是先用Gird Search的方法,得到所有候选参数,然后每次从中随机选择进行训练。 Bayesian Optimization. 贝叶斯优化,考虑到了不同参数对应的实验结果值,因此更节省时间。和网络搜索相比简直就是老牛和跑车的区别。具体原理可以参考这个论文: Practical Bayesian Optimization of Machine Learning Algorithms ,这里同时推荐两个实现了贝叶斯调参的Python库,可以上手即用: jaberg/hyperopt, 比较简单。 fmfn/BayesianOptimization, 比较复杂,支持并行调参。 原文链接:https://blog.csdn.net/anshiquanshu/article/details/77938831 UNIT 3一些大的注意事项 刚开始, 先上小规模数据,模型往大了放,只要不爆显存,能用256个filter你就别用128个。直接奔着过拟合去。没错,就是训练过拟合网络, 连测试集验证集这些都可以不用。如果小数据量下,这么粗暴的大网络奔着过拟合去都没效果,那么有可能是:模型的输入输出是不是有问题? 代码错误? 模型解决的问题定义是不是有问题? 你对应用场景的理解是不是有错?* Loss设计要合理。一般来说分类就是Softmax, 回归就是L2的loss. 但是要注意loss的错误范围(主要是回归), 你预测一个label是10000的值, 模型输出0, 你算算这loss多大, 这还是单变量的情况下. 一般结果都是nan. 所以不仅仅输入要做normalization, 输出也要这么弄。多任务情况下, 各loss想法限制在一个量级上, 或者最终限制在一个量级上, 初期可以着重一个任务的loss。 观察loss胜于观察准确率。准确率虽然是评测指标,但是训练过程中还是要注意loss的。你会发现有些情况下,准确率是突变的,原来一直是0, 可能保持上千迭代, 然后突然变1。要是因为这个你提前中断训练了, 只有老天替你惋惜了. 而loss是不会有这么诡异的情况发生的, 毕竟优化目标是loss。给NN一点时间, 要根据任务留给NN的学习一定空间. 不能说前面一段时间没起色就不管了. 有些情况下就是前面一段时间看不出起色, 然后开始稳定学习。 确认分类网络学习充分。分类网络就是学习类别之间的界限. 你会发现, 网络就是慢慢的从类别模糊到类别清晰的. 怎么发现? 看Softmax输出的概率的分布. 如果是二分类, 你会发现, 刚开始的网络预测都是在0.5上下, 很模糊. 随着学习过程, 网络预测会慢慢的移动到0,1这种极值附近. 所以, 如果你的网络预测分布靠中间, 再学习学习。 Learning Rate设置合理。太大: loss爆炸, 或者nan。太小: 半天loss没反映。需要进一步降低了: loss在当前LR下一路降了下来, 但是半天不再降了。如果上面的Loss设计那块你没法合理, 初始情况下容易爆, 先上一个小LR保证不爆, 等loss降下来了, 再慢慢升LR, 之后当然还会慢慢再降LR。 对比训练集和验证集的loss。 判断过拟合, 训练是否足够, 是否需要early stop的依据。 清楚receptive field的大小。CV的任务, context window是很重要的. 所以你对自己模型的receptive field的大小要心中有数. 这个对效果的影响还是很显著的. 特别是用FCN, 大目标需要很大的receptive field。 简短的注意事项 预处理:-mean/std zero-center就够了, PCA, 白化什么的都用不上。 shuffle, shuffle, shuffle。 网络原理的理解最重要,CNN的conv这块,你得明白sobel算子的边界检测。 Dropout, Dropout, Dropout(不仅仅可以防止过拟合, 其实这相当于做人力成本最低的Ensemble, 当然, 训练起来会比没有Dropout的要慢一点, 同时网络参数你最好相应加一点, 对, 这会再慢一点)。 CNN更加适合训练回答是否的问题, 如果任务比较复杂, 考虑先用分类任务训练一个模型再finetune。 无脑用ReLU(CV领域)。 无脑用3x3。 无脑用xavier。 LRN一类的, 其实可以不用. 不行可以再拿来试试看。 filter数量2^n 多尺度的图片输入(或者网络内部利用多尺度下的结果)有很好的提升效果。 第一层的filter, 数量不要太少. 否则根本学不出来(底层特征很重要)。 sgd adam 这些选择上, 看你个人选择. 一般对网络不是决定性的. 反正我无脑用sgd + momentum。 batch normalization我一直没用, 虽然我知道这个很好, 我不用仅仅是因为我懒. 所以要鼓励使用batch normalization。 不要完全相信论文里面的东西. 结构什么的觉得可能有效果, 可以拿去试试。 你有95%概率不会使用超过40层的模型。 shortcut的联接是有作用的。 原文链接:https://www.imooc.com/article/30562 UNIT 41. 从繁就简具有正规化和学习率(learning rate)调度器的复杂架构的神经网络,较单一神经网络更难调试。首先,构建一个相对简单的模型:构建一个具有单个隐藏层的小模型,并进行验证;然后逐渐添加模型的复杂性,同时检验模型结构的每个层面(附加层、参数等)是否有效。其次,在单个数据节点上训练模型:可以使用一两个训练数据点(data point)以确认模型是否过度拟合。神经网络应立即过度拟合,训练准确率为 100%,这表明模型符合;如果模型无法过度拟合这些数据点,就证明太小或存在 bug。 2. 确认模型损失模型损失是评估模型性能的主要方式,也是模型设置重要参数以进行评估的依据,因此需要确保: 模型损失适用于任务(使用分类交叉熵损失(cross-entropy los)进行多分类问题或使用 focal loss 以解决不平衡问题); 正确衡量损失函数的重要性。如果你使用多种类型的损失函数,如 MSE、对抗性、L1、feature loss,,那么请确保所有损失以正确的方式排序。 3. 检查中间输出和连接为了调试神经网络,你需要理解神经网络内部的动态、不同中间层所起的作用,以及层与层之间是如何连接起来的。不过,你可能遇到以下问题: 不正确的梯度更新表达式 权重未得到应用 梯度消失或爆发 如果梯度值为 0,则意味着优化器中的学习率可能太小,且梯度更新的表达式不正确。 除了关注梯度的绝对值之外,还要确保监视每个层匹配的激活、权重的大小。例如,参数更新的大小(权重和偏差)应为 1-e3。 需要指出的是,一种称为 “Dying ReLU” 或“梯度消失”的现象中,ReLU 神经元在学习其权重的负偏差项后将输出为 0。这些神经元不会在任何数据点上得到激活。 你可以采用梯度检验(gradient checking)通过数值方法逼近梯度以检验这些错误。如果它接近计算梯度,则正确实施反向传播。 关于可视化神经网络的主要方法,Faizan Shaikh 举出了三个例子: 初始方法:展现训练模型的整体结构,这些方法包括展示神经网络各个层的形状或过滤器(filters)以及每个层中的参数; 基于激活的方法:破译单个神经元或一组神经元的激活函数; 基于梯度的方法:在训练模型时,操作由前向或后向通道形成的梯度。 还有有许多可用的工具可用于可视化各个层的激活和连接,例如 ConX 和 Tensorboard。 4. 诊断参数神经网络具有大量彼此相互作用的参数,使得优化也变得非常困难。 Batch size:你希望 batch size 可大到足以准确评估误差梯度,小到足以使随机梯度下降(SGD)可以规范网络。batch size 将导致学习过程在训练过程中以噪声成本快速瘦脸,并可能导致优化困难。 学习率(Learning rate):太低会导致收敛缓慢或陷入局部最小值的风险,太高则会导致优化发散。 机器学习框架,如 Keras、Tensorflow、PyTorch、MXNet 现在都有关于使用学习率收敛缓慢文档或示例: Keras: https://keras.io/callbacks/#learningratescheduler Tensorflow: https://www.tensorflow.org/api_docs/python/tf/train/exponential_decay PyTorch: https://pytorch.org/docs/stable/_modules/torch/optim/lr_scheduler.html MXNet: https://mxnet.incubator.apache.org/versions/master/tutorials/gluon/learning_rate_schedules.html 梯度剪切(Gradient clipping ):在反向传播中,用于剪切参数梯度的最大值或最大范数。 Batch 标准化(normalization ):用于标准化每层的输入,以对抗内部协变量移位问题。 随机梯度下降(Stochastic Gradient Descent ,SGD):使用动量、自适应学习率、Nesterov 更新。 正则化:对于构建可推广模型至关重要,因为它增加了对模型复杂性或极端参数值的惩罚。同时,它显著降低了模型的方差,并且不显著增加偏差。 Dropout:是另一种规范网络以防止过度拟合的技术。在训练时,以某个概率 p(超参数)保持神经元活动来实现丢失,否则将其设置为 0。结果,网络必须在每个训练 batch 中使用不同的参数子集,这减少了特定参数的变化而变得优于其他参数。 全称跟踪工作通过对工作更好地跟踪,可以轻松查看和重现之前的试验,以减少重复工作。不过,手动记录信息可能很难做到且进行多次实验,像 comet.ml 这样的工具可以帮助自动追踪数据集、更改代码、实验历史和生产模型,包括关于模型的关键信息,如超参数、模型性能指标和环境细节。神经网络对数据、参数,甚至 packages 的微小变化都非常敏感,这导致了模型的性能下降。工作跟踪是标准化环境和建模工作流程的第一步。 原文链接:https://mp.weixin.qq.com/s?__biz=MzI0ODcxODk5OA==&mid=2247503598&idx=4&sn=6f0f0d69917dec6479856768deef026f&chksm=e99efd17dee97401940fbde1b827e25af2de0f7a2c976318ca9704167954ecd064f2a0b48f40&mpshare=1&scene=23&srcid=0319l7CrzcTA1SGejFL2ylHO#rd UNIT 51、数据梳理训练神经网络的第一步是不要碰代码,先彻底检查自己的数据。这一步非常关键。我喜欢用大量时间浏览数千个样本,理解它们的分布,寻找其中的模式。幸运的是,人类大脑很擅长做这件事。有一次,我发现数据中包含重复的样本,还有一次我发现了损坏的图像/标签。我会查找数据不均衡和偏差。我通常还会注意自己的数据分类过程,它会揭示我们最终探索的架构。比如,只需要局部特征就够了还是需要全局语境?标签噪声多大?此外,由于神经网络是数据集的压缩/编译版本,你能够查看网络(错误)预测,理解预测从哪里来。如果网络预测与你在数据中发现的不一致,那么一定是什么地方出问题了。在你对数据有了一些感知之后,你可以写一些简单的代码来搜索/过滤/排序标签类型、标注规模、标注数量等,并沿任意轴可视化其分布和异常值。异常值通常能够揭示数据质量或预处理中的 bug。 2、配置端到端训练/评估架构、获取基线结果现在我们已经理解了数据,那我们就可以开始构建高大上的多尺度 ASPP FPN ResNet 并训练强大的模型了吗?当然还不到时候,这是一个充满荆棘的道路。我们下一步需要构建一个完整的训练、评估架构,并通过一系列实验确定我们对准确率的置信度。在这个阶段,你们最好选择一些不会出错的简单模型,例如线性分类器或非常精简的 ConvNet 等。我们希望训练这些模型,并可视化训练损失、模型预测和其它度量指标(例如准确率)。当然在这个过程中,我们还需要基于一些明确假设,从而执行一系列对照实验(ablation experiments)。该阶段的一些技巧与注意事项: 固定随机seed:始终使用固定的随机 seed 能保证很多属性,例如在我们两次运行相同代码时能得到相同的输出。这能消除变化因子,从进行合理的判断。 简化:确保禁用不必要的技巧。例如,在这个阶段肯定需要关闭数据增强。数据增强可以在后期引入,并作为一种强大的正则化策略。不过在这个阶段引入的话,它就有机会带来一些愚蠢的 bug。 使用多数据、少次数的验证评估:当我们在绘制测试损失时,我们需要在整个比较大的测试集中执行评估。不要过几个批量就绘制一次测试损失,然后再依赖 TensorBoard 的平滑处理。我们虽然追求的是准确率,但也要防止犯这些低级错误。 在初始化中验证损失:验证你的损失函数在初始化中有比较合理的损失值。例如,如果你正确地初始化最终层,那么你应该通过-log(1/n_classes) 度量初始化的 Softmax 值。L2 回归和 Huber 损失函数等都有相同的默认值。 优秀的初始化:正确地初始化最终层。例如,如果你正在对均值为 50 的一些数据做回归处理,那么初始化的最终偏置项就应该为 50。如果你有一个非平衡数据集(两类样本数 1:10),那么就需要在 logits 上设置偏置项,令模型在初始化时预测概率为 0.1。正确配置这些偏置项将加快收敛速度,因为网络在前面几次迭代中基本上只在学习偏置。 人类基线结果:监控损失值等其他度量指标(例如准确度),这些指标应该是人类能解释并检查的。尽可能评估你自己(人类)获得的准确率,并与构建的模型做对比。或者对测试数据进行两次标注,其中一次为预测值,另一次为标注值。 独立于输入的基线结果:训练一个独立于输入的基线模型,例如最简单的方法就是将所有输入都设置为 0。这样的模型应该比实际输入数据表现更差,你的模型是否准备好从任何输入中抽取任何信息? 在批数据上过拟合:在单个批数据上使得过拟合(两个或多个少样本)。为此,我们需要增加模型拟合能力,并验证我们能达到的最低损失值(即 0)。我还想在同一张图中显示标签和预测值,并确保损失值一旦达到最小,它们就能完美地对齐了。 验证训练损失的下降:在这一阶段,你可能希望在数据集上实现欠拟合,该阶段的模型应该是极简的。然后我们尝试增加一点模型的拟合能力,再看看训练损失是否稍微下降了一些。 在输入网络前可视化:在运行模型之前,我们需要可视化数据。也就是说,我们需要可视化输入到网络的具体数据,即可视化原始张量的数据和标签。这是唯一的「真实来源」,我有很多次都是因为这个过程而节省了大量时间,并揭示了数据预处理和数据增强过程中的问题。 可视化预测过程:我喜欢在训练过程中对一个固定的测试批数据进行模型预测的可视化。这展示了预测值如何变化的过程,能为我们提供关于训练过程的优秀直觉。很多时候,如果网络以某种方式小幅度波动,那么模型最可能在尝试拟合数据,这也展示了一些不稳定性。太低或太高的学习率也很容易注意到,因为抖动量比较大。 使用反向传播绘制依赖性:你的深度学习代码通常包括复杂的、矢量化的、Boardcast 操作。一个常见的 bug 是,人们会无意间使用 view 而不是 transpose/permute,从而混合了批量数据中的维度信息。然而,你的网络仍然可以正常训练,只不过它们学会忽略了其它样本中的数据。一种 debug 的方法是将某些样本 i 的损失设置为 1.0,然后运行反向传播一直到输入,并确保第 i 个样本的梯度不为零。更一般的,梯度为我们提供了网络中的依赖性关系,它们在 debug 中非常有用。 一般化特殊案例:这是一种更为通用的代码技巧,但是我经常看到人们在使用这些技巧时会新产生 Bug,尤其是在从头构建一般函数时。相反,我喜欢直接写非常具体的函数,它只包含我现在需要做的事情。我会先让这个函数能 work,然后再一般化好函数,并确保能取得相同的结果。通常这个过程会体现在向量化代码中,我会先用循环编写某个过程,然后再一次一个循环地将它们转化为向量化化代码。 3、过拟合到了这个阶段,我们应该对数据集有所了解了,而且有了完整的训练+评估流程。对于任何给定的模型,我们可以计算出我们信任的度量。而且还为独立于输入的基线准备了性能,一些 dumb 基线的性能(最好超过这些),我们人类的表现有大致的了解(并希望达到这一点)。现在,我们已经为迭代一个好的模型做好了准备。我准备用来寻找好模型的方法有两个阶段:首先获得足够大的模型,这样它能够过拟合(即关注训练损失),然后对其进行适当的正则化(弃掉一些训练损失以改进验证损失)。我喜欢这两个阶段的原因是,如果我们不能用任何模型实现较低的误差率,则可能再次表明一些问题、bug 和配置错误。该阶段的一些技巧与注意事项: 选择模型:为了达到理想的训练损失,我们可能希望为数据选择一个合适的架构。当我们在挑选模型时,我的第一个建议即别好高骛远。我看到很多人都非常渴望一开始就堆叠一些新的模块,或创造性地用于各种异质架构,从而想一步到位做好。我建议可以找最相关的论文,并直接利用它们的简单架构,从而获得良好性能。后面再基于这个架构做修改和改进,并将我们的想法加进去就行了。 Adam是一般选择:在配置基线模型地早期阶段,我喜欢使用 Adam 算法(学习率为 3e-4)。在我的经验中,Adam 对超参数的容忍度更高,不太好的学习率也能获得一般的效果。对于卷积网络来说,一般经过仔细调整的 SGD 几乎总会略优于 Adam,但最佳学习率的可能区域要窄得多。 一次复杂化一个:如果你有多个特性插入分类器,我建议你一个个插入,从而确保能获得期待的性能提升。不要在最开始时就一次性全加上,这样你会弄不清楚性能提升到底是哪个特性带来的。还有其它增加复杂性的方法,例如你可以先尝试插入较小的图像,然后再慢慢地加大。 别相信默认的学习率衰减:如果你修改来自其它领域的代码,你应该小心使用学习率衰减方法。对于不同问题,你不仅希望使用不同的衰减策略,同时因为 Epoch 的数量不同,衰减过程也会不一样。例如数据集的大小,会影响 Epoch 的数量,而很多学习率衰减策略是直接与 Epoch 相关的。在我自己的工作中,我经常整个地关闭学习率衰减,即使用常数学习率。 4、正则化理想情况下,我们现在至少有了一个拟合训练集的大模型。现在是时候对它进行正则化,并通过放弃一些训练准确率来提升验证准确率了。技巧包括: 更多数据:首先,在当前任何实际环境中正则化模型的最好方式是增加更多真实的训练数据。在你能收集更多数据时,花费大量工程时间试图从小数据集上取得更好结果是很常见的一个错误。我认为增加更多数据是单调提升一个较好配置神经网络性能的唯一可靠方式。 数据增强:比真实数据较次的方法是半假数据,试验下更激进的数据增强。 创造性增强:如果半假数据也没有,假数据也还可以。人们在寻求扩展数据集的创造性方法。例如,域随机化、使用模拟数据、把数据插入场景这样机智的混合方法,甚至可以用 GAN。 预训练:即使你有足够的数据,你也可以使用预训练网络,基本没什么损失。 坚持监督式学习:不要对无监督学习过于激动。据我所知,没有什么无监督学习方法在当前计算机视觉任务上有很强的结果(尽管 NLP 领域现在有了 BERT 和其他类似模型,但这更多归功于文本更成熟的本质以及对噪声比更好的信号)。 更小的输入维度:移除可能包含假信号的特征。如果你的数据集很小,任何加入的假输入只会增加过拟合的可能。类似地,如果低级细节作用不大,试试输入更小的图像。 更小的模型:在许多情况下,你可以在网络上使用域知识约束来降低模型大小。例如,在 ImageNet 主干网络顶部使用全连接层一度很流行,但它们后来被简单的平均池化取代,消除了这一过程中大量的参数。 减小批大小:由于 BN 基于批量大小来做归一化,较小的批量大小具有更强的正则化效果。这主要因为一个批量的统计均值与标准差是实际均值和标准差的近似,所以缩放量和偏移量在小批量内波动地更大。 drop:增加 dropout。在卷积网络上使用 dropout2d(空间 dropout)。保守谨慎的使用 dropout,因为它对 batch 归一化好像不太友好。 权重衰减:增加权重衰减惩罚。 早停(early stopping):基于你得到的验证损失停止训练,从而在即将过拟合之前获取模型。 尝试更大的模型:我过去多次发现更大模型最终都会很大程度的过拟合,但它们「早停」后的性能要比小模型好得多。 最后,为了更加确保网络是个合理的分类器,我喜欢可视化网络第一层的权重,确保自己获得了有意义的边缘。如果第一层的滤波器看起来像噪声,那需要去掉些东西。类似地,网络内的激活函数有时候也会揭示出一些问题。 5、精调现在你应该位于数据集一环,探索取得较低验证损失的架构模型空间。这一步的一些技巧包括: 随机网格搜索:在同时精调多个超参数时,使用网格搜索听起来更诱惑,能够确保覆盖到所有环境。但记住,使用随机搜索反而是最佳方式。直观上,因为神经网络对一些参数更为敏感。在极限情况下,如果参数 a 很重要,改变 b 却没有影响,然后相比于多次在固定点采样,你宁可彻底采样a。 超参数优化:如今社区内有大量好的贝叶斯超参数优化工具箱,我的一些朋友用过后觉得很成功。但我的个人经验是,探索好的、宽的模型空间和超参数的最佳方法是找个实习生。开玩笑而已,哈哈哈。 6、最后的压榨一旦你找到最好的架构类型和超参数,依然可以使用更多的技巧让系统变得更好: 集成:模型集成是能将准确率稳定提升 2% 的一种好方式。如果你承担不起测试阶段的计算成本,试着使用《Distilling the Knowledge in a Neural Network》中的方法把你的模型蒸馏到一个网络。 一直训练:我经常看到一些人在验证损失趋平时会中断模型训练,以我的经验来看,网络会长时间保持非直观的训练。寒假时有一次我忘了关掉模型训练,一月回来后发现它取得了 SOTA 结果。 原文链接:https://mp.weixin.qq.com/s?__biz=MzA3MzI4MjgzMw==&mid=2650761143&idx=1&sn=e39d7da6124ab2c2516688f67cdb793c&chksm=871aa3c9b06d2adf6d13a3601468e2791a0350863a20bc7bd17713bf2f70f087810711c47512&mpshare=1&scene=23&srcid=0510T8zlZRVwq1aBT62cNGlM#rd","categories":[{"name":"机器学习文摘","slug":"机器学习文摘","permalink":"http://yoursite.com/categories/机器学习文摘/"}],"tags":[{"name":"深度学习","slug":"深度学习","permalink":"http://yoursite.com/tags/深度学习/"},{"name":"调参","slug":"调参","permalink":"http://yoursite.com/tags/调参/"},{"name":"经验","slug":"经验","permalink":"http://yoursite.com/tags/经验/"}]},{"title":"AboutMe","slug":"AboutMe","date":"2019-03-01T01:46:39.000Z","updated":"2019-03-01T02:30:48.000Z","comments":true,"path":"2019/03/01/AboutMe/","link":"","permalink":"http://yoursite.com/2019/03/01/AboutMe/","excerpt":"","text":"关于站长 Ph.D candidate Nation Space Science Center, Chinese Academy of Sciences, Beijing, China E-mail: lixudong16@mails.ucas.ac.cn Wechat ID: wuan3076 Research Artificial intelligence: Machine Learning, Transfer learning, CNN, etc. Prognostic and Health Management (PHM): Application of machine learning in PHM Education 2016.09-Now: Ph.D candidate in Computer Application Technology.Nation Space Science Center. CAS, Beijing, China 2012.09-2016.06: B.E in Automation.Harbin Engineering University. Harbin, Heilongjiang Skills C/C++/C# Python Deeplearning Framework : Keras, Tensorflow Papers Lin Y, Li X, Hu Y. Deep diagnostics and prognostics: An integrated hierarchical learning framework in PHM applications[J]. Applied Soft Computing, 2018. Issues and Tips:A set of Integrated Experiments of Applying Auto-Encoder and Convolutional Neural Network in Feature Extraction and Faut Diagnosis.Xudong Li, Mingtao Li, Jianhua Zheng, Yang Hu*, PHM-Chongqing Conference 2018 Person Love reading books. Novel, science fiction, history, psychological, philosophy, physical, Design, etc. A Photographers. Like landscape photography. Physics & astronomy enthusiasts, once wanted to be an astronomer. Like graphic design, familiar with PhotoShop. Have a little talent for painting. Hope to keep it :)","categories":[],"tags":[{"name":"AboutMe","slug":"AboutMe","permalink":"http://yoursite.com/tags/AboutMe/"}]},{"title":"使用hexo+github搭建博客","slug":"使用hexo+github搭建博客","date":"2019-02-26T07:12:08.000Z","updated":"2019-08-16T10:19:20.000Z","comments":true,"path":"2019/02/26/使用hexo+github搭建博客/","link":"","permalink":"http://yoursite.com/2019/02/26/使用hexo+github搭建博客/","excerpt":"","text":"前言之前我使用过CSDN、简书等平台,还买域名和虚拟空间使用Z-Blog自己搭过网站,每个平台各有优缺点。CSDN很大众化,就是计算机技术类的平台,文章也容易在百度中被搜索到,阅读量很容易上去。不过CSDN的文本编辑器实在不好用。简书的Markdown很好用,排版优美,但文章不容易被搜索到,阅读量很难上去,而且简书的文章非常杂,什么娱乐八卦的都有。使用域名+虚拟空间的方式自己搭建,灵活度很大,但域名和空间需要花钱,自己建站过程也繁琐,也不容易被搜索到。后来发现github也可以搭建自己的博客,使用github pages服务搭建博客的好处有: 全是静态文件,访问速度快; 免费方便,不用花一分钱就可以搭建一个自由的个人博客,不需要服务器不需要后台; 可以随意绑定自己的域名,不仔细看的话根本看不出来你的网站是基于github的; 数据绝对安全,基于github的版本管理,想恢复到哪个历史版本都行; 博客内容可以轻松打包、转移、发布到其它平台; 不过也不容易被搜索到。没有十全十美的平台,找到适合自己的就行。在github搭建博客需要用到,Hexo是一个简单、快速、强大的基于 Github Pages的博客发布工具,支持Markdown格式,有众多优秀插件和主题。 准备工作 注册Github账号。点这里进入github主页,注册很简单,略过。 安装node.js。要使用Hexo,需要安装node.js。点击这里选择合适的安装包下载安装。安装过程参考这里。 安装git。使用git往github上传代码进行版本控制。点击这里下载git。 在github创建仓库 在github首页的右上角,点击加号,选择‘New repository’ 在Repository name一栏,仓库的名字一定是‘你的用户名+github.io’,比如你的github的用户名为xyz,那么仓库名为xyz.github.io,否则无法通过xyz.github.io访问。 创建完成后,打开仓库的setting,查看GitHub Pages,发现已默认开启。 配置SSH Key。SSH key用来解决本地和服务器的连接问题,获取提交代码至github的权限。打开之前安装好的Git Bash,输入: cd ~/.ssh 如果提示:No such file or directory 说明你是第一次使用git。然后输入: ssh-keygen -t rsa -C "邮件地址" 根据提示操作,会提示你输入密码。最终会生成一个在c盘用户目录下的文件,打开用户目录,找到.ssh\\id_rsa.pub文件,记事本打开并复制里面的内容。在github进入个人设置 -> SSH and GPG keys -> New SSH key: 将刚才复制的内容粘贴到key那里,title自己定,然后点击Add SSH Key按钮保存。 测试是否成功。在git bash里输入: $ ssh -T git@github.com # 注意邮箱地址不用改 如果提示Are you sure you want to continue connecting (yes/no)?,输入yes,然后会看到 Hi 5663015! You've successfully authenticated, but GitHub does not provide shell access. 说明SSH配置成功。最后配置用户名和注册邮箱: $ git config --global user.name "liuxianan"// 你的github用户名,非昵称 $ git config --global user.email "xxx@qq.com"// 填写你的github注册邮箱 使用Hexo写博客1、安装hexo $ npm install -g hexo 2、初始化。在电脑的某个地方新建一个文件夹,这个文件夹就是你博客的所在文件夹,名字自己定。比如这个文件夹路径为F:\\hexo,输入以下命令,hexo会自动下载一些文件到这个目录。 $ cd /f/Workspaces/hexo/ $ hexo init 3、然后执行: $ hexo g $ hexo s 执行hexo g命令之后,hexo就会在public文件夹生成相关html文件,这些文件将来都是要提交到github去的: hexo s是开启本地预览服务,打开浏览器访问 http://localhost:4000 即可看到内容。 4、新建文章。执行以下命令: hexo new "first-blog" #新建文章,引号内为文章名,可自定义 新建的文章在hexo\\source\\_posts目录下,为.md文件,使用markdown语法编辑。打开文件,默认有以下内容: --- title: first-blog date: 2019-03-02 15:30:16 tags: --- 我们可以自定义以下以下一些信息: --- title: first-blog #文章页面上的显示名称 date: 2019-03-02 15:30:16 #文章生成时间,一般不改,当然也可以任意修改 categories: 默认分类 #分类,自定义 tags: [tag1,tag2,tag3] #文章标签,可空,多标签用[]括起来 description: 附加一段文章摘要,字数最好在140字以内,会出现在meta的description里面 --- 5、上传至github。在第三步里看到的博客首页,是在本地显示的,要在网上看到,需要上传至github。首先安装一个插件: npm install hexo-deployer-git --save 然后打开根目录下的_config.yml文件,配置关于deploy的部分: deploy: type: git repository: git@github.com:5663015/5663015.github.io.git #此处为SSH地址 branch: master 然后输入: hexo g hexo d 最终上传至github,打开之前的网址即可看到博客。 更改主题默认主题比较一般,我们可以更换其他主题。官方主题提供了很多漂亮的主题,进去选择你喜欢的主题,并找到其github地址。比如本博客使用的是hexo-theme-snippet主题,地址为https://github.com/shenliyang/hexo-theme-snippet,以此为例,首先下载此主题: $ git clone https://github.com/shenliyang/hexo-theme-snippet 下载后的主题在这里,保存在theme文件夹里: 修改_config.yml中的theme: landscape改为theme: yilia,然后重新执行hexo g来重新生成。如果出现一些莫名其妙的问题,可以先执行hexo clean来清理一下public的内容,然后再来重新生成和发布。 在hexo中支持数学公式在hexo中,你会发现我们不能用 Latex 语法来书写数学公式,这给我们写博客带来了很多不便。通过安装第三方库可以解决这个问题。 1、使用Kramed代替 Marked hexo默认的渲染引擎是marked,但是marked不支持mathjax。 kramed是在marked的基础上进行修改,我们在工程目录下执行以下命令来安装kramed: npm uninstall hexo-renderer-marked --save npm install hexo-renderer-kramed --save 然后,更改/node_modules/hexo-renderer-kramed/lib/renderer.js,更改 // Change inline math rule function formatText(text) { // Fit kramed's rule: $$ + \\1 + $$ return text.replace(/`\\$(.*?)\\$`/g, '$$$$$1$$$$'); } 为: // Change inline math rule function formatText(text) { return text; } 2、停止使用 hexo-math 如果已经安装hexo-math,首先卸载它: npm uninstall hexo-math --save 然后安装mathjax: npm install hexo-renderer-mathjax --save 3、更新 Mathjax 的 CDN 链接 打开/node_modules/hexo-renderer-mathjax/mathjax.html,然后,把<script>更改为: <script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-MML-AM_CHTML"></script> 4、更改默认转义规则 因为hexo默认的转义规则会将一些字符进行转义,比如_转为<em>, 所以我们需要对默认的规则进行修改。首先,打开/node_modules/kramed/lib/rules/inline.js: escape: /^\\\\([\\\\`*{}\\[\\]()#$+\\-.!_>])/, 更改为: escape: /^\\\\([`*\\[\\]()# +\\-.!_>])/, 将: em: /^\\b_((?:__|[\\s\\S])+?)_\\b|^\\*((?:\\*\\*|[\\s\\S])+?)\\*(?!\\*)/, 更改为: em: /^\\*((?:\\*\\*|[\\s\\S])+?)\\*(?!\\*)/, 5、开启mathjax 在主题_config.yml中开启Mathjax,找到mathjax字段添加如下代码: mathjax: enable: true 默认所有文章的mathjax都是开启的,在加载时会比较慢。如果有的文章没有公式,可以关闭这些文章的mathjax,可以在文章里设置mathjax为false: --- title: Testing Mathjax with Hexo category: Uncategorized date: 2017/05/03 mathjax: true --- 最后hexo g并hexo d上传即可使用mathjax书写公式了。","categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"}],"tags":[{"name":"github","slug":"github","permalink":"http://yoursite.com/tags/github/"},{"name":"hexo","slug":"hexo","permalink":"http://yoursite.com/tags/hexo/"},{"name":"博客","slug":"博客","permalink":"http://yoursite.com/tags/博客/"}]}],"categories":[{"name":"原创教程","slug":"原创教程","permalink":"http://yoursite.com/categories/原创教程/"},{"name":"随笔","slug":"随笔","permalink":"http://yoursite.com/categories/随笔/"},{"name":"机器学习文摘","slug":"机器学习文摘","permalink":"http://yoursite.com/categories/机器学习文摘/"}],"tags":[{"name":"算法","slug":"算法","permalink":"http://yoursite.com/tags/算法/"},{"name":"数据结构","slug":"数据结构","permalink":"http://yoursite.com/tags/数据结构/"},{"name":"深度学习","slug":"深度学习","permalink":"http://yoursite.com/tags/深度学习/"},{"name":"CNN","slug":"CNN","permalink":"http://yoursite.com/tags/CNN/"},{"name":"自动化","slug":"自动化","permalink":"http://yoursite.com/tags/自动化/"},{"name":"NAS","slug":"NAS","permalink":"http://yoursite.com/tags/NAS/"},{"name":"AutoDL","slug":"AutoDL","permalink":"http://yoursite.com/tags/AutoDL/"},{"name":"机器学习","slug":"机器学习","permalink":"http://yoursite.com/tags/机器学习/"},{"name":"数学","slug":"数学","permalink":"http://yoursite.com/tags/数学/"},{"name":"概率论","slug":"概率论","permalink":"http://yoursite.com/tags/概率论/"},{"name":"逻辑回归","slug":"逻辑回归","permalink":"http://yoursite.com/tags/逻辑回归/"},{"name":"github","slug":"github","permalink":"http://yoursite.com/tags/github/"},{"name":"贝叶斯","slug":"贝叶斯","permalink":"http://yoursite.com/tags/贝叶斯/"},{"name":"自编码器","slug":"自编码器","permalink":"http://yoursite.com/tags/自编码器/"},{"name":"线性模型","slug":"线性模型","permalink":"http://yoursite.com/tags/线性模型/"},{"name":"模型评估","slug":"模型评估","permalink":"http://yoursite.com/tags/模型评估/"},{"name":"理论","slug":"理论","permalink":"http://yoursite.com/tags/理论/"},{"name":"迁移学习","slug":"迁移学习","permalink":"http://yoursite.com/tags/迁移学习/"},{"name":"书单","slug":"书单","permalink":"http://yoursite.com/tags/书单/"},{"name":"论文写作","slug":"论文写作","permalink":"http://yoursite.com/tags/论文写作/"},{"name":"矩阵论","slug":"矩阵论","permalink":"http://yoursite.com/tags/矩阵论/"},{"name":"原创","slug":"原创","permalink":"http://yoursite.com/tags/原创/"},{"name":"文献","slug":"文献","permalink":"http://yoursite.com/tags/文献/"},{"name":"项目","slug":"项目","permalink":"http://yoursite.com/tags/项目/"},{"name":"文摘","slug":"文摘","permalink":"http://yoursite.com/tags/文摘/"},{"name":"编程语言","slug":"编程语言","permalink":"http://yoursite.com/tags/编程语言/"},{"name":"python","slug":"python","permalink":"http://yoursite.com/tags/python/"},{"name":"PHM专栏","slug":"PHM专栏","permalink":"http://yoursite.com/tags/PHM专栏/"},{"name":"文献汇总","slug":"文献汇总","permalink":"http://yoursite.com/tags/文献汇总/"},{"name":"会议","slug":"会议","permalink":"http://yoursite.com/tags/会议/"},{"name":"论文","slug":"论文","permalink":"http://yoursite.com/tags/论文/"},{"name":"框架","slug":"框架","permalink":"http://yoursite.com/tags/框架/"},{"name":"代码","slug":"代码","permalink":"http://yoursite.com/tags/代码/"},{"name":"实践","slug":"实践","permalink":"http://yoursite.com/tags/实践/"},{"name":"文档","slug":"文档","permalink":"http://yoursite.com/tags/文档/"},{"name":"ppt","slug":"ppt","permalink":"http://yoursite.com/tags/ppt/"},{"name":"教程","slug":"教程","permalink":"http://yoursite.com/tags/教程/"},{"name":"竞赛","slug":"竞赛","permalink":"http://yoursite.com/tags/竞赛/"},{"name":"计算机视觉","slug":"计算机视觉","permalink":"http://yoursite.com/tags/计算机视觉/"},{"name":"机器学习, 文摘","slug":"机器学习,-文摘","permalink":"http://yoursite.com/tags/机器学习,-文摘/"},{"name":"随笔","slug":"随笔","permalink":"http://yoursite.com/tags/随笔/"},{"name":"梦想","slug":"梦想","permalink":"http://yoursite.com/tags/梦想/"},{"name":"调参","slug":"调参","permalink":"http://yoursite.com/tags/调参/"},{"name":"经验","slug":"经验","permalink":"http://yoursite.com/tags/经验/"},{"name":"AboutMe","slug":"AboutMe","permalink":"http://yoursite.com/tags/AboutMe/"},{"name":"hexo","slug":"hexo","permalink":"http://yoursite.com/tags/hexo/"},{"name":"博客","slug":"博客","permalink":"http://yoursite.com/tags/博客/"}]}