首页
博客统计
微语
博客APP
友情链接
更多
精美壁纸
留言板
扶贫计划
关于博主和投稿
推荐
植物大战僵尸
网站优化
高考倒计时
小霸王游戏机
烟花模拟器
忘心主页
在线PS
Search
1
防举报——QQ打开链接跳转浏览器代码
8,252 阅读
2
中国疫情和全球疫情实时地图
7,040 阅读
3
QQ卡片实现红包强制进群代码
6,022 阅读
4
利用html写APP公告、更新的文章
5,986 阅读
5
2021年一款好看的高考倒计时源码
5,212 阅读
新闻资讯
技术教程
闲言碎语
小曲配故事
话题探讨
源码
站外篇
杂乱分享
登录
/
注册
Search
标签搜索
Typecho
代码
源码
swap
热歌推荐
话题
api
小程序
web
机器人
引流
Linux
新冠实时疫情
忘心留言箱
HTML
一言
备份
红包卡片
代刷
宝塔
忘心
累计撰写
113
篇文章
累计收到
279
条评论
首页
栏目
新闻资讯
技术教程
闲言碎语
小曲配故事
话题探讨
源码
站外篇
杂乱分享
页面
博客统计
微语
博客APP
友情链接
精美壁纸
留言板
扶贫计划
关于博主和投稿
推荐
植物大战僵尸
网站优化
高考倒计时
小霸王游戏机
烟花模拟器
忘心主页
在线PS
用户登录
登录
注册
搜索到
36
篇与
的结果
2020-10-27
技术心得丨一种有效攻击BERT等模型的方法
Is BERT Really Robust? A Strong Baseline for Natural Language Attack on Text Classification and Entailment作者机构:MIT,香港大学,A* STAR论文发表:AAAI2020论文连接:http://aaai.org/Papers/AAAI/2020GB/AAAI-JinD.7014.pdf概要:机器学习模型对对抗样本敏感,在对抗样本上效果下降明显。本文提出了一个生成对抗样本的模型,TEXTFOOLER。通过替换样本中的重要词汇获得对抗样本,在对抗样本上模型的效果急剧下降。该方法可以用于数据增广,提升模型的鲁棒性和泛化能力。背景介绍对抗样本攻击是指通过某种方法生成一些样本,已经训练好的模型在这些生成的对抗样本上的效果急剧下降,模型非常脆弱。对抗样本攻击在计算机视觉领域研究的比较多,但是文本领域相对较少。本文提出了一种对抗样本生成模型,TEXTFOOLER,可以有效的生成对抗样本,并且生成的样本在语法、语义上都比较合理,其计算复杂度是线性的。方法TEXTFOOLER输入:是候选样本X、样本的标注标签Y、已经训练好的模型F、句子相似度计算模型Sim,句子相似度阈值、整个语料词典对应的词向量Emb输出:候选样本的对抗样本,即新生成的样本。主要分两步:第一步:词重要性排序,即获得基于重要性分数排序的词集合W。第二步:对抗样本生成,即基于集合W对原始样本进行词替换获得对抗样本。1. 词重要性排序目标是获得输入样本中每个词在模型预测过程中的重要性。Equation 1 词重要性分数词的重要性分数按上述公式计算,即如果对抗样本标签原始样本标签一样,则重要性分数等于模型预测值得差值,若标签不一样,则重要性分数为标签为原始标签的模型预测值差值和标签为预测标签的模型预测值差值之和。得到每个词的重要性分数后,基于NLTK和spaCy过滤掉停用词,获得最终的词重要性排序集合W。2. 对抗样本生成目标是找到最终的每个词的替换词并用替换词替换样本得到最终的对抗样本集合。1)同义词提取:对W中的每个词wj,根据词向量从词典中找到Top N的同义词,并通过词性过滤后得到候选替换词集合CANDIDATES。2)句子相似度检查:对CANDIDATES中每个词ck,用ck替换wj得到新的对抗样本 同时计算原始样本X和对抗样本之间的相似度 (通过Universal Sentence Encoder得到句子的向量表示,然后计算余弦距离作为相似度)。作为两个句子的语义相似度。相似度高于给定阈值的替换词放进最终的替换词候选集合FINCANDIDATES.3)对于FINCANDIDATES的每个词,如果有候选词改变了模型的预测的类别,那么选择句子相似度最大的词作为最终候选词。如果没有改变模型的预测类别,选择预测置信度最低的词作为最终的替换词。4)重复1)-3)的操作。图 1 生成的对抗样本的例子实验结果实验数据主要包含: 文本分类任务:预测文本的某个标签。 文本蕴含任务:预测文本对中两个句子的关系,即蕴含关系、矛盾关系或者中性。 图 2 在分类任务上的对抗结果图 3 在文本蕴含上的对抗结果结果:对测试集进行对抗样本替换后,准确率急剧下降,甚至到0.和其他对抗模型比较图 4 和其他对抗模型比较结论:从替换词比例和攻击成功率(模型预测错误的比例)两个维度都比基线模型好。人工评价人工评价对抗样本的语法、人工标签、是否保留了原始样本的语义这三个维度。结论:对抗样本语法合理,人工标签和原始样本标签在MR数据集上一致率达92%,句子语义相似度达0.91.控制变量实验通过控制变量的方法验证各个步骤对模型效果的影响。词重要性排序通过取消词重要性排序的步骤看该步骤对模型效果的影响。图 5 取消词重要性排序的结果(Random)结论:词重要性排序很重要。语义相似度约束通过取消候选替换词中的语义相似度约束看该步骤对模型效果的影响。图 6 语义相似度约束对比 “/”前后表示有和无语义相似度约束的结果对比结论:语义相似度约束对结果影响很大。可迁移性由一个模型生成的对抗样本是否可以使得其他模型出错。图 7 对抗样本的可迁移性。行i,列j表示模型i生成的对抗样本在模型j上的准确率结论:模型效果越好,基于该模型生成的对抗样本的可迁移性越高。对抗训练生成的对抗样本可以用于训练模型,增强模型的鲁棒性和泛化能力。图 8 基于对抗样本的对抗训练结果结论:对抗训练可显著提高模型效果。启发:1. 可以通过此方法生成对抗样本可以用于数据增广,加入到训练数据中来增强模型的鲁棒性和泛化能力。2. 可通过文字的重要性词汇排序方法筛选标签相关的主题词汇,如构建情感词典、主题词挖掘、关键词挖掘等。 点击关注,第一时间了解华为云新鲜技术~
2020年10月27日
987 阅读
0 评论
0 点赞
2020-10-27
(数据科学学习手札97)掌握pandas中的transform
本文示例文件已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes1 简介 开门见山,在pandas中,transform是一类非常实用的方法,通过它我们可以很方便地将某个或某些函数处理过程(非聚合)作用在传入数据的每一列上,从而返回与输入数据形状一致的运算结果。 本文就将带大家掌握pandas中关于transform的一些常用使用方式。 图12 pandas中的transform 在pandas中transform根据作用对象和场景的不同,主要可分为以下几种:2.1 transform作用于Series 当transform作用于单列Series时较为简单,以前段时间非常流行的企鹅数据集为例: 图2我们在读入数据后,对bill_length_mm列进行transform变换: 单个变换函数 我们可以传入任意的非聚合类函数,譬如对数化:# 对数化 penguins['bill_length_mm'].transform(np.log) 图3 或者传入lambda函数:# lambda函数 penguins['bill_length_mm'].transform(lambda s: s+1) 图4 多个变换函数 也可以传入包含多个变换函数的列表来一口气计算出多列结果:penguins['bill_length_mm'].transform([np.log, lambda s: s+1, np.sqrt]) 图5 而又因为transform传入的函数,在执行运算时接收的输入参数是对应的整列数据,所以我们可以利用这个特点实现诸如数据标准化、归一化等需要依赖样本整体统计特征的变换过程:# 利用transform进行数据标准化 penguins['bill_length_mm'].transform(lambda s: (s - s.mean()) / s.std()) 图62.2 transform作用于DataFrame 当transform作用于整个DataFrame时,实际上就是将传入的所有变换函数作用到每一列中:# 分别对每列进行标准化 ( penguins .loc[:, 'bill_length_mm': 'body_mass_g'] .transform(lambda s: (s - s.mean()) / s.std()) ) 图7 而当传入多个变换函数时,对应的返回结果格式类似agg中的机制,会生成MultiIndex格式的字段名:( penguins .loc[:, 'bill_length_mm': 'body_mass_g'] .transform([np.log, lambda s: s+1]) ) 图8 而且由于作用的是DataFrame,还可以利用字典以键值对的形式,一口气为每一列配置单个或多个变换函数:# 根据字典为不同的列配置不同的变换函数 ( penguins .loc[:, 'bill_length_mm': 'body_mass_g'] .transform({'bill_length_mm': np.log, 'bill_depth_mm': lambda s: (s - s.mean()) / s.std(), 'flipper_length_mm': np.log, 'body_mass_g': [np.log, np.sqrt]}) ) 图92.3 transform作用于DataFrame的分组过程 在对DataFrame进行分组操作时,配合transform可以完成很多有用的任务,譬如对缺失值进行填充时,根据分组内部的均值进行填充:# 分组进行缺失值均值填充 ( penguins .groupby('species')[['bill_length_mm', 'bill_depth_mm', 'flipper_length_mm', 'body_mass_g']] .transform(lambda s: s.fillna(s.mean().round(2))) ) 图10 并且在pandas1.1.0版本之后为transform引入了新特性,可以配合Cython或Numba来实现更高性能的数据变换操作,详细的可以阅读( https://github.com/pandas-dev/pandas/pull/32854 )了解更多。 除了以上介绍的内容外,transform还可以配合时间序列类的操作譬如resample等,功能都大差不差,感兴趣的朋友可以自行了解。 以上就是本文的全部内容,欢迎在评论区与我进行讨论
2020年10月27日
1,085 阅读
0 评论
0 点赞
2020-10-26
【译】自动发现 .NET 5 中代码的潜在错误
写代码是一件令人兴奋的事情,特别是对于 .NET 开发人员来说,平台越来越智能化了。我们现在默认在 .NET SDK 中包含丰富的诊断和代码建议。在您需要安装 NuGet 包或其他独立工具来进行更多的代码分析之前。现在,您将在新的 .NET 5 SDK 中自动获得这些内容。 过去,我们一直不愿意向 C# 添加新的警告。这是因为,对于将警告视为错误的用户来说,添加新的警告从技术上来说是一种对源代码的影响。然而,这些年来,在我们遇到的很多情况中,我们也确实想警告人们有些地方出了问题,从常见的编码错误到常见的 API 误用等等。 从 .NET 5 开始,我们在 C# 编译器中引入了 AnalysisLevel,以一种安全的方式引入新的警告。所有针对 .NET 5 的项目的 AnalysisLevel 默认将被设置为 5,这意味着将引入更多的警告(以及修复它们的建议)。 让我们讨论一下 AnalysisLevel 可能的值在您的项目中意味着什么。首先我们要注意的是:除非你覆盖默认值,否则 AnalysisLevel 是基于你的目标框架设置的: 目标框架 默认值 net5.0 5 netcoreapp3.1 or lower 4 netstandard2.1 or lower 4 .NET Framework 4.8 or lower 4 但是,0-3 呢?下面是对每个分析级别值含义的更详细的细分: AnalysisLevel 对C#编译器的影响 高级平台API分析 5 获得新的编译器语言分析(详细内容如下) Yes 4 与之前版本中向 C# 编译器传递 -warn:4 相同 No 3 与之前版本中向 C# 编译器传递 -warn:3 相同 No 2 与之前版本中向 C# 编译器传递 -warn:2 相同 No 1 与之前版本中向 C# 编译器传递 -warn:1 相同 No 0 与之前版本中向 C# 编译器传递 -warn:0 一样,关闭所有发出警告 No 由于 AnalysisLevel 与项目的目标框架绑定在一起,除非你改变了你的代码目标框架,否则你永远不会改变默认的分析级别。不过,你可以手动设置分析级别。例如,即使我们的目标是 .NET Core App 3.1 或 .NET Standard (因此 AnalysisLevel 默认为 4),你仍然可以选择更高的级别。这里有一个例子: Exe netcoreapp3.1 5 如果你想要最高的分析级别,你可以在你的项目文件中指定 latest: Exe netcoreapp3.1 latest 如果你很有冒险精神,并且希望尝试实验性的编译器和平台分析,那么可以指定 preview 来获得最新的、最前沿的代码诊断。 请注意,当您使用 latest 或 preview 时,分析结果可能会因机器而异,这取决于可用的 SDK 和它提供的最高分析级别。 Exe netcoreapp3.1 preview 最后,也可以设置为 none,这意味着“我不想看到任何新的警告”。在这种模式下,你不会得到任何高级 API 分析,也不会得到新的编译器警告。如果你需要更新框架,但还没有准备好接受新的警告,那么这将非常有用。 Exe net5 none 你还可以在 Visual Studio 中通过 Code Analysis 属性页配置项目的分析级别。只需从解决方案资源管理器导航到项目属性页。然后转到 Code Analysis 选项卡。 在未来,我们将为 .NET 的每个版本添加一个新的分析级别。目标是确保给定的分析级别总是表示相同的默认分析集(规则及其严重性)。如果我们想在默认情况下启用现有的规则,我们将在即将到来的分析级别中这样做,而不是更改现有的级别。这确保了已有的项目/源代码总是产生相同的警告,不管 SDK 有多新(当然,除了项目使用 preview 或 latest)。 由于所有的 .NET 5 项目都将进入分析级别 5,让我们来看看一些新的警告和建议。分析级别 5 中出现的所有新的警告和错误 粗体部分将在 .NET 5 发布的时候进入第 5 级。剩下的是 Visual Studio 2019 16.8 预览2 中的 .NET 5 预览8 中的新警告! 常见错误的警告 第一组新的警告旨在发现潜在的错误,通常是在较大的代码库中。现在不需要额外的编译器分析就可以很容易地发现它们。当表达式永真或永假时发出警告 这种新的警告非常普遍,考虑以下代码:public voidM(DateTime dateTime) {if (dateTime == null) //warning CS8073 {return; } } DateTime 是一个结构体,结构体不能为空。从 .NET 开始,我们将在 CS8073 中警告这种情况。警告信息是: Warning CS8073: The result of the expression is always ‘false’ since the value of type ‘DateTime’ is never equal to ‘null’ of type ‘DateTime?’ 很明显,这段代码所做的事情没有意义,但是考虑到这样的检查可能发生在有多个参数要验证的方法中。要解决这个问题,你可以删除代码(因为它总是假的,它没有做任何事情),或者改变它的类型为 DateTime? 如果参数的预期值为 null。public void M(DateTime? dateTime) //We accept a null DateTime {if (dateTime == null) //No Warnings {return; } }不允许在静态类型上用as、 is 下面是一个很好的小改进:static classFiz { }classP {bool M(objecto) {return o is Fiz; //CS7023 } } 因为 Fiz 是一个静态类,所以像 o 这样的实例对象永远不可能是这种类型的实例。我们会收到这样的警告: Warning CS7023 The second operand of an ‘is’ or ‘as’ operator may not be static type ‘Fiz’ 解决这个问题的方法是重构我们的代码(也许我们一开始就检查错类型了),或者让类 Fiz 是非静态的:classFiz { }classP {bool M(objecto) {return o is Fiz; //no error } }不允许锁定非引用类型 锁定非引用类型(比如 int)什么也做不了,因为它们是按值传递的,所以每个堆栈帧上都有不同版本的非引用类型。在过去,对于像 lock(5) 这样简单的情况,我们会警告你对非引用类型的锁定,但是直到最近,我们对泛型方法的也支持警告:public classP {public static void GetValue(TKey key) {lock (key) //CS0185 { } }static voidMain() { GetValue(1); } } 这是一个错误,因为传入 int(在这个不受约束的泛型中允许)实际上不会正确锁定。我们会看到这个错误: Error CS0185 ‘TKey’ is not a reference type as required by the lock statement 要解决这个问题,我们需要指出 GetValue 方法应该只提供引用类型。我们可以使用泛型类型约束来做到这一点,where TKey : classpublic classP {public static void GetValue(TKey key) where TKey : class{lock (key) //no error { } } }重新抛出以保留堆栈细节 我们都是“优秀的”开发人员,所以我们的代码不会抛出异常,对吗?好吧,即使是最好的开发人员也需要处理异常,而新程序员常陷入的一个陷阱是:try{throw newException(); }catch(Exception ex) {//probably logging some info here...//rethrow now that we are done throw ex; //CA2200 } 在学校里,我学到如果有人向我扔球,我接住它,我必须把球扔回去!像这样的比喻让很多人相信 throw ex 是重新抛出这个异常的正确方式。遗憾的是,这将改变原来异常中的堆栈。现在您将收到一个警告,说明正在发生这种情况。它是这样的: Warning CA2200 Re-throwing caught exception changes stack information 在几乎所有情况下,这里要做的正确事情是简单地使用 throw 关键字,而不提及我们捕获的异常的变量。try{throw newException(); }catch(Exception ex) {//probably logging some info here...//rethrow now that we are done throw; } 我们还提供了一个代码修复,可以轻松地在您的文档、项目或解决方案中一次性修复所有这些问题!不要在值类型中使用 ReferenceEquals Equality 在 .NET 中是一个棘手的话题。下一个警告试图使意外地通过引用比较一个 struct 。考虑以下代码:int int1 = 1;int int2 = 1; Console.WriteLine(object.ReferenceEquals(int1, int2)); //warning CA2013 这将装箱两个 int,而 ReferenceEquals 将总是返回 false 作为结果。我们将看到这个警告描述: Warning CA2013: Do not pass an argument with value type ‘int’ to ‘ReferenceEquals’. Due to value boxing, this call to ‘ReferenceEquals’ will always return ‘false’. 解决此错误的方法是使用相等运算符 == 或 object.Equals:int int1 = 1;int int2 = 1; Console.WriteLine(int1== int2); //using the equality operator is fine Console.WriteLine(object.Equals(int1, int2)); //so is object.Equals跟踪跨程序集中结构的明确赋值(definite assignment) 很多人可能会惊讶地发现,下一个警告其实并不算是警告:usingSystem.Collections.Immutable;classP {public void M(out ImmutableArray immutableArray) //CS0177 { } } 这条规则是关于明确赋值的,这是 C# 中一个有用的特性,可以确保你不会忘记给变量赋值。 Warning CS0177: The out parameter ‘immutableArray’ must be assigned to before control leaves the current method 目前已经针对几种不同的情况发布了 CS0177,但不是前面展示的情况。这里的历史是,这个 bug 可以追溯到 C# 编译器的原始实现。以前,C# 编译器在计算明确赋值时忽略从元数据导入的值类型中的引用类型的私有字段。这个非常特殊的错误意味着像 ImmutableArray 这样的类型能够逃脱明确赋值分析。 现在编译器将正确的显示错误,你可以修复它,只要确保它总是分配一个值,像这样:usingSystem.Collections.Immutable;classP {public bool M(out ImmutableArray immutableArray) //no warning { immutableArray= ImmutableArray.Empty; } }.NET API 使用错误的警告 下面示例是关于正确使用 .NET 库的。分析级别可以防止现有的 .NET API 的不当使用,但它也会影响 .NET 库的发展。如果设计了一个有用的 API,但它有可能被误用,那么还可以在新增 API 的同时添加一个检测误用的新警告。不要给从 MemoryManager 的派生类定义终结器 当你想实现自己的 Memory 类型时,MemoryManager 是一个有用的类。这不是你经常做的事情,但是当你需要它的时候,你真的需要它。这个新的警告会触发这样的情况:class DerivedClass : MemoryManager{public override bool Dispose(booldisposing) {if(disposing) { _handle.Dispose(); } }~DerivedClass() => Dispose(false); //warning CA2015 } 向这种类型添加终结器可能会在垃圾收集器中引入漏洞,这是我们都希望避免的! Warning CA2015 Adding a finalizer to a type derived from MemoryManager may permit memory to be freed while it is still in use by a Span. 解决方法是删除这个终结器,因为它会在你的程序中导致非常细微的 bug,很难找到和修复。class DerivedClass : MemoryManager{public override bool Dispose(booldisposing) {if(disposing) { _handle.Dispose(); } }//No warning, since there is no finalizer here }参数传递给 TaskCompletionSource,调用错误的构造函数 这个警告告诉我们,我们使用了错误的枚举。var tcs = new TaskCompletionSource(TaskContinuationOptions.RunContinuationsAsynchronously); //warning CA2247 除非你已经意识到这个问题,否则你可能会在盯着它看一会儿才能发现。问题是这样的,这个构造函数不接受 TaskContinuationOptions 枚举,它接受 TaskCreationOptions 枚举。发生的事情是,我们正在调用的 TaskCompletionSource 的构造函数接受 object 类型参数!考虑到它们的名称特别相似,并且它们的值也非常相似,所以这种错误容易发生。 Warning CA2247: Argument contains TaskContinuationsOptions enum instead of TaskCreationOptions enum. 修复它只需要传递正确的枚举类型:var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); //no warning当代码不能在所有平台上工作时发出警告 这个真是太棒了!我不会在这里详细讨论它的复杂之处(期待以后关于这个主题的博客文章)。但这里警告的目的是让您知道,您正在调用的 api 可能无法在您正在构建的所有目标上工作。 假设我有一个同时在 Linux 和 Windows 上运行的应用程序。我有一个方法,我使用它来获得路径来创建日志文件,根据运行环境,它有不同的行为。private static stringGetLoggingPath() {var appDataDirectory =Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);var loggingDirectory = Path.Combine(appDataDirectory, "Fabrikam", "AssetManagement", "Logging");//Create the directory and restrict access using Windows//Access Control Lists (ACLs). var rules = new DirectorySecurity(); //CA1416 rules.AddAccessRule(new FileSystemAccessRule(@"fabrikam\log-readers", FileSystemRights.Read, AccessControlType.Allow) ); rules.AddAccessRule(new FileSystemAccessRule(@"fabrikam\log-writers", FileSystemRights.FullControl, AccessControlType.Allow) );if (!OperatingSystem.IsWindows()) {//Just create the directory Directory.CreateDirectory(loggingDirectory); }else{ Directory.CreateDirectory(loggingDirectory, rules); }returnloggingDirectory; } 我正确地使用了 OperatingSystem.IsWindows() 来检查操作系统是否是 Windows 操作系,但是实际上 if 分支之前已经使用了平台特定的 API,将不能工作在 Linux! Warning CA1416: ‘DirectorySecurity’ is unsupported on ‘Linux’ 处理这个问题的正确方法是将所有特定于平台的代码移动到 else 语句中。private static stringGetLoggingPath() {var appDataDirectory =Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);var loggingDirectory = Path.Combine(appDataDirectory, "Fabrikam", "AssetManagement", "Logging");if (!OperatingSystem.IsWindows()) {//Just create the directory Directory.CreateDirectory(loggingDirectory); }else{//Create the directory and restrict access using Windows//Access Control Lists (ACLs). var rules = new DirectorySecurity(); //CA1416 rules.AddAccessRule(new FileSystemAccessRule(@"fabrikam\log-readers", FileSystemRights.Read, AccessControlType.Allow) ); rules.AddAccessRule(new FileSystemAccessRule(@"fabrikam\log-writers", FileSystemRights.FullControl, AccessControlType.Allow) ); Directory.CreateDirectory(loggingDirectory, rules); }returnloggingDirectory; }低级编码帮助 在编写高性能应用程序时,还有一些有用的警告。下面这些警告确保您不需要为这些情况牺牲安全性。P/Invoke 时不要在 string 参数上使用 OutAttribute 有时你需要与本地代码进行互操作。.NET 有使用平台调用服务的概念(P/ Invoke)来简化这个过程。但是,在 .NET 中,在向本地库发送数据和从本地库发送数据方面存在一些问题。考虑以下代码:[DllImport("MyLibrary")]private static extern void Goo([Out] string s); //warning CA1417 除非您非常熟悉 P/Invoke 的编写,否则这里的错误并不明显。通常将 OutAttribute 应用于运行时不知道的类型,以指示应该如何封送类型。OutAttribute 表示您正在按值传递数据。字符串按值传递没有意义,而且有可能导致运行时崩溃。 Warning CA1417 Do not use the ‘OutAttribute’ for string parameter ‘s’ which is passed by value. If marshalling of modified data back to the caller is required, use the ‘out’ keyword to pass the string by reference instead. 解决这个问题的方法是将其作为一个普通的 out 参数(通过引用传递)来处理。[DllImport("MyLibrary")]private static extern void Goo(out string s); //no warning 或者如果你不需要将字符串封送回调用者,你可以这样做:[DllImport("MyLibrary")]private static extern void Goo(string s); //no warning在适当情况下,string 使用 AsSpan 而不是基于范围的索引器 这都是为了确保您不会意外地分配字符串。classProgram {public void TestMethod(stringstr) { ReadOnlySpan slice = str[1..3]; //CA1831 } } 在上面的代码中,开发者的意图是使用 C# 中新的基于范围的索引特性来索引一个字符串。不幸的是,这实际上会分配一个字符串,除非您首先将该字符串转换为 span。 Warning CA1831 Use ‘AsSpan’ instead of the ‘System.Range’-based indexer on ‘string’ to avoid creating unnecessary data copies 解决方法是在这种情况下添加 AsSpan 调用:classProgram {public void TestMethod(stringstr) { ReadOnlySpan slice = str.AsSpan()[1..3]; //no warning } }不要在循环中使用 stackalloc stackalloc 关键字非常适合于确保正在进行的操作对垃圾收集器来说比较容易。在过去,stackalloc 关键字用于不安全的代码上下文中,以便在堆栈上分配内存块。但自从 C# 8 以来,它也被允许在 unsafe 的块之外,只要这个变量被分配给一个 Span 或一个 ReadOnlySpan。classC {public void TestMethod(stringstr) {int length = 3;for (int i = 0; i < length; i++) { Span numbers = stackalloc int[length]; //CA2014 numbers[i] =i; } } } 在堆栈上分配大量内存可能会导致著名的 StackOverflow 异常,即我们在堆栈上分配的内存超过了允许的范围。在循环中分配尤其危险。 Warning CA2014 Potential stack overflow. Move the stackalloc out of the loop. 解决方法是将 stackalloc 移出循环:classC {public void TestMethod(stringstr) {int length = 3; Span numbers = stackalloc int[length]; //no warning for (int i = 0; i < length; i++) { numbers[i]=i; } } }设置分析级别 现在您已经看到了这些警告的重要性,您可能永远不想回到一个没有它们的世界,对吗?我知道世界并不总是这样运转的。正如在这篇文章的开头提到的,这些都是打破源代码的改变,你应该在适合自己的时间表中完成它们。我们现在介绍这个的部分原因是为了得到两个方面的反馈: 我们提出的这一小部分警告是否太过破坏性 调整警告的机制是否足以满足您的需要 回到 .NET Core 3.1 的分析等级 如果你只想回到 .NET 5 之前的状态(即.NET Core 3.1 中的警告级别),你所需要做的就是在你的项目文件中将分析级别设置为4。下面是一个例子: Exe net5.0 4 只关闭一个规则 如果有一个你认为不适用于你的代码库的特定警告,你可以使用一个 editorconfig 文件来关闭它。你可以通过在错误列表中将警告的严重性设置为“none”来做到这一点。 或者从编辑器中出现警告的灯泡菜单中选择“None”关闭警告的单个实例 如果你大部分时间都想使用这个警告,但在少数情况下要关闭它,你可以使用灯泡菜单中的一个: 在源码中禁止 在单独的禁止文件中禁止它 在源码中禁用并标记一个特性总结 我们希望你对 .NET 5 代码分析的改进感到兴奋,请给我们一些反馈。原文链接 https://devblogs.microsoft.com/dotnet/automatically-find-latent-bugs-in-your-code-with-net-5/
2020年10月26日
1,028 阅读
0 评论
0 点赞
2020-10-26
Spring Boot 系列:最新版优雅停机详解
爱生活,爱编码,本文已收录架构技术专栏关注这个喜欢分享的地方。开源项目: 分布式监控(Gitee GVP最有价值开源项目 ):https://gitee.com/sanjiankethree/cubic 摄像头视频流采集:https://gitee.com/sanjiankethree/cubic-video 优雅停机目前Spring Boot已经发展到了2.3.4.RELEASE,伴随着2.3版本的到来,优雅停机机制也更加完善了。目前版本的Spring Boot 优雅停机支持Jetty, Reactor Netty, Tomcat和 Undertow 以及反应式和基于 Servlet 的 web 应用程序都支持优雅停机功能。优雅停机的目的:如果没有优雅停机,服务器此时直接直接关闭(kill -9),那么就会导致当前正在容器内运行的业务直接失败,在某些特殊的场景下产生脏数据。增加了优雅停机配置后:在服务器执行关闭(kill -2)时,会预留一点时间使容器内部业务线程执行完毕,此时容器也不允许新的请求进入。新请求的处理方式跟web服务器有关,Reactor Netty、 Tomcat将停止接入请求,Undertow的处理方式是返回503.新版配置YAML配置新版本配置非常简单,server.shutdown=graceful 就搞定了(注意,优雅停机配置需要配合Tomcat 9.0.33(含)以上版本)server: port: 6080 shutdown: graceful #开启优雅停机 spring: lifecycle: timeout-per-shutdown-phase: 20s #设置缓冲时间 默认30s 在设置了缓冲参数timeout-per-shutdown-phase 后,在规定时间内如果线程无法执行完毕则会被强制停机。下面我们来看下停机时,加了优雅停日志和不加的区别://未加优雅停机配置 Disconnected from the target VM, address: '127.0.0.1:49754', transport: 'socket' Process finished with exit code 130 (interrupted by signal 2: SIGINT) 加了优雅停机配置后,可明显发现的日志 Waiting for active requests to cpmplete,此时容器将在ShutdownHook执行完毕后停止。关闭方式1、 一定不要使用kill -9 操作,使用kill -2 来关闭容器。这样才会触发java内部ShutdownHook操作,kill -9不会触发ShutdownHook。2、可以使用端点监控 POST 请求 /actuator/shutdown 来执行优雅关机。添加ShutdownHook通过上面的日志我们发现Druid执行了自己的ShutdownHook,那么我们也来添加下ShutdownHook,有几种简单的方式:1、实现DisposableBean接口,实现destroy方法@Slf4j @Service public class DefaultDataStore implements DisposableBean { private final ExecutorService executorService = new ThreadPoolExecutor(OSUtil.getAvailableProcessors(), OSUtil.getAvailableProcessors() + 1, 1, TimeUnit.MINUTES, new ArrayBlockingQueue(200), new DefaultThreadFactory("UploadVideo")); @Override public void destroy() throws Exception { log.info("准备优雅停止应用使用 DisposableBean"); executorService.shutdown(); } } 2、使用@PreDestroy注解@Slf4j @Service public class DefaultDataStore { private final ExecutorService executorService = new ThreadPoolExecutor(OSUtil.getAvailableProcessors(), OSUtil.getAvailableProcessors() + 1, 1, TimeUnit.MINUTES, new ArrayBlockingQueue(200), new DefaultThreadFactory("UploadVideo")); @PreDestroy public void shutdown() { log.info("准备优雅停止应用 @PreDestroy"); executorService.shutdown(); } } 这里注意,@PreDestroy 比 DisposableBean 先执行关闭原理1、使用kill pid关闭,源码很简单,大家可以看下GracefulShutdown private void doShutdown(GracefulShutdownCallback callback) { List connectors = getConnectors(); connectors.forEach(this::close); try { for (Container host : this.tomcat.getEngine().findChildren()) { for (Container context : host.findChildren()) { while (isActive(context)) { if (this.aborted) { logger.info("Graceful shutdown aborted with one or more requests still active"); callback.shutdownComplete(GracefulShutdownResult.REQUESTS_ACTIVE); return; } Thread.sleep(50); } } } } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } logger.info("Graceful shutdown complete"); callback.shutdownComplete(GracefulShutdownResult.IDLE); } 2、使用端点监控 POST 请求 /actuator/shutdown关闭因为actuator 都使用了SPI的扩展方式,所以我们看下AutoConfiguration,可以看到关键点就是ShutdownEndpoint@Configuration( proxyBeanMethods = false ) @ConditionalOnAvailableEndpoint( endpoint = ShutdownEndpoint.class ) public class ShutdownEndpointAutoConfiguration { public ShutdownEndpointAutoConfiguration() { } @Bean( destroyMethod = "" ) @ConditionalOnMissingBean public ShutdownEndpoint shutdownEndpoint() { return new ShutdownEndpoint(); } } ShutdownEndpoint,为了节省篇幅只留了一点重要的@Endpoint( id = "shutdown", enableByDefault = false ) public class ShutdownEndpoint implements ApplicationContextAware { @WriteOperation public Map shutdown() { if (this.context == null) { return NO_CONTEXT_MESSAGE; } else { boolean var6 = false; Map var1; try { var6 = true; var1 = SHUTDOWN_MESSAGE; var6 = false; } finally { if (var6) { Thread thread = new Thread(this::performShutdown); thread.setContextClassLoader(this.getClass().getClassLoader()); thread.start(); } } Thread thread = new Thread(this::performShutdown); thread.setContextClassLoader(this.getClass().getClassLoader()); thread.start(); return var1; } } private void performShutdown() { try { Thread.sleep(500L); } catch (InterruptedException var2) { Thread.currentThread().interrupt(); } this.context.close(); //这里才是核心 } } 在调用了 this.context.close() ,其实就是AbstractApplicationContext 的close() 方法 (重点是其中的doClose())/** * Close this application context, destroying all beans in its bean factory. * Delegates to {@code doClose()} for the actual closing procedure. * Also removes a JVM shutdown hook, if registered, as it's not needed anymore. * @see #doClose() * @see #registerShutdownHook() */ @Override public void close() { synchronized (this.startupShutdownMonitor) { doClose(); //重点:销毁bean 并执行jvm shutdown hook // If we registered a JVM shutdown hook, we don't need it anymore now: // We've already explicitly closed the context. if (this.shutdownHook != null) { try { Runtime.getRuntime().removeShutdownHook(this.shutdownHook); } catch (IllegalStateException ex) { // ignore - VM is already shutting down } } } } 后记到这里,关于单机版本的Spring Boot优雅停机就说完了。为什么说单机?因为大家也能发现,在关闭时,其实只是保证了服务端内部线程执行完毕,调用方的状态是没关注的。不论是Dubbo还是Cloud 的分布式服务框架,需要关注的是怎么能在服务停止前,先将提供者在注册中心进行反注册,然后在停止服务提供者,这样才能保证业务系统不会产生各种503、timeout等现象。好在当前Spring Boot 结合Kubernetes已经帮我们搞定了这一点,也就是Spring Boot 2.3版本新功能Liveness(存活状态) 和Readiness(就绪状态)简单的提下这两个状态: Liveness(存活状态):Liveness 状态来查看内部情况可以理解为health check,如果Liveness失败就就意味着应用处于故障状态并且目前无法恢复,这种情况就重启吧。此时Kubernetes如果存活探测失败将杀死Container。 Readiness(就绪状态):用来告诉应用是否已经准备好接受客户端请求,如果Readiness未就绪那么k8s就不能路由流量过来。
2020年10月26日
965 阅读
0 评论
0 点赞
2020-10-24
最简单入门深度学习
最简单入门深度学习该篇文档基于kaggle course,通过简单的理论介绍、程序代码、运行图以及动画等来帮助大家入门深度学习,既然是入门,所以没有太多模型推导以及高级技巧相关,都是深度学习中最基础的内容,希望大家看过之后可以自己动手基于Tensorflow或者Keras搭建一个处理回归或者分类问题的简单的神经网络模型,并通过dropout等手段优化模型结果;每部分都有对应的练习,练习都是很有针对性的,而且都很有趣,尤其是一些练习中都写好了动画的可视化展示,还是很有心的;目录: 概述 线性模型:单神经元 非线性模型:深度神经网络 模型训练:随机梯度下降 验证模型:过拟合和欠拟合 提升性能:Dropout和Batch Normalization 分类问题 概述经过本篇文章,你将搭建自己的深度神经网络,使用Keras和Tensorflow,创建全连接神经网络,在分类和回归问题上应用神经网络,通过随机梯度下降训练网络、通过dropout等技术提升模型性能;近些年在AI方面的主要发展都在深度学习,尤其是应用于自然语言处理、图像识别、游戏AI等领域,深度学习能得到更接近于人类的结果;深度学习是一种允许大量深度计算为特征的机器学习方法,深度计算使得深度学习模型可以理解真实世界数据中的复杂和高维的信息模式,比如这句话的含义是什么、这张图中的人在干嘛等等;通过这种优势和灵活性,神经网络成为深度学习的定义模型,神经网络由神经元组成,每个神经元单独看只是一个简单的计算单元,神经网络的能力来自于许多神经元之间的复杂的组合模式;单个神经元线性单元只有一个输入的线性单元对应公式如下:y = w*x+bx为输入,神经元连接的权重为w,w的更新就是神经网络学习的过程,b为偏差,它与输入没有关系,偏差允许神经元不依赖输入来修改输出,y是神经元的输出,即公式y=w*x+b的结果;线性单元作为模型的例子神经元通常作为神经网络的一部分,往往也会将一个单独的神经元模型作为基准模型,单神经元模型是线性模型;假设我们使用糖分作为输入训练模型,卡路里作为输出,假设偏差b为90,权重w为2.5,当糖分为5时,卡路里为2.5*5+90=102.5;多个输入当我们期望使用多个输入而不是一个时,其实就是将多个输入连接并神经元,计算每个连接权重,并全部加起来得到最终输出,如下:y = w_0x_0 + w_1x_1 + w_2*x_2 + b上述公式使用了三个输入,并分别对应各自的连接权重,从输入维度上看,单个输入拟合一条直线,两个输入你和一个平面,多个输入拟合的则是超平面;Keras中使用线性单元最简单的创建线性单元模型是通过keras.Sequential,可以通过dense层来创建上述提到的线性单元模型,对于一个有三个输入,一个输出的线性模型,Keras创建方式如下:from tensorflow import keras from tensorflow.keras import layers # Create a network with 1 linear unit model = keras.Sequential([ layers.Dense(units=1, input_shape=[3]) ]) 其中units为1表示该层只有一个输出,input_shape为[3]则表示有3个输入,之所以参数是个列表[],这是因为在图像领域可能需要三维输入,比如[高度,宽度,通道];线性单元练习可以通过这个notebook来进行这部分的练习,里面包含了如何通过keras搭建线性单元的神经元模型,并通过其weights属性来查看模型的连接权重和偏差,最后还有一个未训练的模型在预测中的表现,可以看到其随机权重在每次运行结果都不一样;深度神经网络层典型的神经网络通过层来组织他们的神经元,当我们把线性单元整理到一起时,我们就得到了一个dense层,神经网络通过叠加dense层来将输入以越来越复杂的方式进行转换,在一个训练好的神经网络模型,每一层都会将输入转换的更接近结果一点;激活函数激活函数作用于层的输出,最常用的是整流函数max(0,x),纠正函数将负部分处理为0,当我们将整流函数应用于一个线性单元时,也就得到了ReLU,而之前的线性公式:y=w*x+b也变成了:y = max(0, w*x+b)可以看到,函数也从线性转为了非线性,整流函数图像如下:堆叠dense层输出层之前通常有一些隐含层,一般我们不能直接看到他们的输出(因为他们的输出并不是最后输出,而是作为下一层的输入,因此无法直接看到),注意当处理回归问题时,最后一层也就是输出层是线性单元,也就是没有应用激活函数,当我们要处理分类或者其他问题时,仍然需要对应的激活函数;通过keras.Sequential创建多层神经网络方式很简单,只要从第一层到最后一层依次通过layer定义即可,第一层获取输入,最后一层产生输出,代码如下:from tensorflow.keras import layers model = keras.Sequential([ # the hidden ReLU layers layers.Dense(units=4, activation='relu', input_shape=[2]), layers.Dense(units=3, activation='relu'), # the linear output layer layers.Dense(units=1), ]) 其中各个layer表示各个堆叠的网络层,activation表示各个层的激活函数,可以看到最后一层是没有的,这是因为它处理的是回归问题,且最后一层输出只有一个,而其他层则不一定;深度神经网络练习你可以通过这个notebook来进行这部分练习,其中包含如何通过keras.Sequential搭建3个隐含层1个输出层的非线性神经网络模型,以及如何使用单独的激活层来代替activation参数,以及ReLU、eLU、SeLU、swish等各个激活函数的差异,实验证明ReLU适用于大多数场景,因此最适合作为初始激活函数选择,下面给出各个接获函数的图像:relu:elu:selu:swish:随机梯度下降在之前创建的神经网络模型中,网络中的权重都是随机指定的,此时的模型还没有学习到任何东西,这也是第一个练习中每次运行结果都不一样的原因;所谓训练一个神经网络,指的是通过某种方式不断更新网络中的权重,使得模型通过输入可以得到期望的输出,如果可以做到,那么也说明了这些权重在某种程度上表达了输入特征与输出之间的关系;训练模型需要两个必要元素: 损失函数:衡量模型预测结果好坏; 优化方法:指导模型如何去修改权重; 损失函数损失函数用于衡量模型的预测值与真实值之间的差异,不同的问题使用的损失函数一般也是不同的,例如对于回归问题,即我们要预测的是数值,一个常用的用于回归问题的损失函数为MAE,即平均绝对误差,对于每个预测值y_pred,MAE计算它与y_true的差值的绝对值,所有这些绝对值取平均就是MAE的结果,除了MAE,用于回归问题的还有很多损失函数,比如MSE、MASE、Huber loss等等,对于模型来说,在训练过程中,损失函数起到向导的作用,最小化损失函数就是模型要解决的问题,以此来指导网络中权重的更新方向;优化方法 - 随机梯度下降通过损失函数我们确定了模型要解决的问题,但是依然需要告知模型如何去解决这个问题,此时就需要一种优化方法,优化方法是一种最小化损失的算法;实际上所有应用于深度学习的优化算法都属于随机梯度下降族,它们都是迭代算法,一步一步的训练模型,每一步的训练过程如下: 抽样部分训练数据,通过模型运行得到预测结果y_pred; 测量这些y_pred与y_true之间的损失函数值; 通过损失更小的方向来修改权重; 上述过程一遍一遍的运行,直到损失为0或者损失无法再下降为止;迭代中从训练集中抽样的部分称之为minibatch,或者一般直接叫做batch,每一轮完整的训练称之为epoch,epoch的数量决定了模型使用各个数据点的次数;理想的训练过程中,权重不断更新,损失不断减少,预测值越来越接近于真实值;学习率和Batch Size学习率决定了模型在每一个batch上学习到的内容的大小,学习率越小意味着模型需要更多的batch来帮助其学习,学习率和batch size是两个训练过程中影响很大的参数,通常也是主要要调的超参数;可惜的是,对于很多情况下都没有必要通过非常耗时的超参数调整来获取最优的结果,Adam是一种不需要设置学习率的随机梯度下降算法,它不需要调试任何参数,或者说它是自调整的,因此它成为一种很好的通用优化方法;添加损失函数和优化方法在定义模型后,可以通过模型的compile方法添加损失函数和优化方法:model.compile( optimizer="adam", loss="mae", ) 例子 - 红酒品质数据格式如下,最后一列为预测目标列: fixed acidity volatile acidity citric acid residual sugar chlorides free sulfur dioxide total sulfur dioxide density pH sulphates alcohol quality 10.8 0.470 0.43 2.10 0.171 27.0 66.0 0.99820 3.17 0.76 10.8 6 8.1 0.820 0.00 4.10 0.095 5.0 14.0 0.99854 3.36 0.53 9.6 5 9.1 0.290 0.33 2.05 0.063 13.0 27.0 0.99516 3.26 0.84 11.7 7 10.2 0.645 0.36 1.80 0.053 5.0 14.0 0.99820 3.17 0.42 10.0 6 可以看到,除了最后一列总有11列作为输入,神经网络搭建代码如下:from tensorflow import keras from tensorflow.keras import layers model = keras.Sequential([ layers.Dense(512, activation='relu', input_shape=[11]), layers.Dense(512, activation='relu'), layers.Dense(512, activation='relu'), layers.Dense(1), ]) 看到网络由3个隐含层和1个输出层组成,其中隐含层的units均为512,表示每个隐含层输出都有512个,第一层负责接受输入,最后一层输出结果;定义完了网络结构,下面需要设置训练需要使用的损失函数和优化方法:model.compile( optimizer='adam', loss='mae', ) 任务为回归预测,损失函数选择平均绝对误差,优化器使用adam;训练前的准备已经就绪,下面需要告诉模型训练使用的batch数量、迭代次数等信息:history = model.fit( X_train, y_train, validation_data=(X_valid, y_valid), batch_size=256, epochs=10, ) 对于训练过程中的loss进行可视化后可以更好的观察模型的整个迭代过程:import pandas as pd # convert the training history to a dataframe history_df = pd.DataFrame(history.history) # use Pandas native plot method history_df['loss'].plot(); 可以看到,在迭代次数达到6次时,后续的迭代中loss的下降不明显,甚至还有变大的情况出行,一般来说这说明迭代次数足够了;模型训练练习这部分练习可以通过这个notebook,其中包含了完整的神经网络模型,从定义到设置其损失和优化方法,再到最后的训练过程,并通过很有趣的动画方式展示了在不同的学习率、batch size、样本数量等情况下的模型迭代过程,对于理解各个参数的作用非常有帮助哦,这里展示其中一组参数下的训练过程:过拟合和欠拟合过拟合和欠拟合是机器学习中绕不开的两个问题,通常我们可以使用学习曲线来观察模型迭代表现并判断其当前属于过拟合还是欠拟合,通常来说过拟合指的是模型过于复杂,将数据中的噪声部分也拟合了,因此使得模型在真实数据上的表现明显差于在训练集的表现,而欠拟合则指的是模型在训练集上都没有达到足够好的效果,可能是因为模型太简单,也可能是因为数据量太大;容量容量指的是模型可以学习到的数据模式的复杂度大小,或者说容量越大的模型,越能深入的理解数据,对于神经网络来说,可以通过增加其宽度和高度来扩大其模型容量;所谓增大网络宽度指的是增加已有层中的神经元个数,而增大高度指的是增加新的层,一般来说使用同样的神经元个数,增加高度带来的容量增益要大于增加宽度,简单理解如下:假设当前网络有两层,每一层都有3个神经元,则其组合为3*3=9,此时我们要增加2个神经元: 如果是用于增加宽度,每层增加一个神经元变为4个,则有4*4=16; 如果是用于增加高度,增加一个单独的层,有2个神经元,则有3*3*2=18; 因此都是使用了两个神经元,从结果上看是高度的收益更大,当然这个只是一种直观理解,实际的解释要比这个复杂的多; 提前停止训练对于模型训练过程,尤其是基于真实数据的训练过程,很多时候是无法完全收敛的,而我们需要保证训练一定可以结束而不是无限运行下去的,因此可以通过Early Stopping来控制其迭代在满足某些条件下提前结束;增加Early Stoppingkeras通过callback的方式添加Early Stopping,所谓callback指的是在每次epoch后运行的内容,用于判断是否应该终止训练过程:from tensorflow.keras.callbacks import EarlyStopping early_stopping = EarlyStopping( min_delta=0.001, # minimium amount of change to count as an improvement patience=20, # how many epochs to wait before stopping restore_best_weights=True, ) 上述代码的含义是,如果连续20次迭代,每次的loss下降都不足0.001,那么训练终止,反正目前为止表现最好的权重数据;例子 - 使用Early Stopping训练模型还是之前的红酒例子,数据格式如下: fixed acidity volatile acidity citric acid residual sugar chlorides free sulfur dioxide total sulfur dioxide density pH sulphates alcohol quality 10.8 0.470 0.43 2.10 0.171 27.0 66.0 0.99820 3.17 0.76 10.8 6 8.1 0.820 0.00 4.10 0.095 5.0 14.0 0.99854 3.36 0.53 9.6 5 9.1 0.290 0.33 2.05 0.063 13.0 27.0 0.99516 3.26 0.84 11.7 7 10.2 0.645 0.36 1.80 0.053 5.0 14.0 0.99820 3.17 0.42 10.0 6 模型定义、指定loss和优化器、指定Early Stopping代码如下:from tensorflow import keras from tensorflow.keras import layers from tensorflow.keras.callbacks import EarlyStopping early_stopping = EarlyStopping( min_delta=0.001, # minimium amount of change to count as an improvement patience=20, # how many epochs to wait before stopping restore_best_weights=True, ) model = keras.Sequential([ layers.Dense(512, activation='relu', input_shape=[11]), layers.Dense(512, activation='relu'), layers.Dense(512, activation='relu'), layers.Dense(1), ]) model.compile( optimizer='adam', loss='mae', ) history = model.fit( X_train, y_train, validation_data=(X_valid, y_valid), batch_size=256, epochs=500, callbacks=[early_stopping], verbose=0, # turn off training log ) history_df = pd.DataFrame(history.history) history_df.loc[:, ['loss', 'val_loss']].plot(); print("Minimum validation loss: {}".format(history_df['val_loss'].min())) 以上,通过fit方法的callbacks参数将Early Stopping作为一个callback添加到了迭代过程中,用于控制训练的提前结束,运行图如下:结合代码和上图可以看到,虽然我们设置了epoch为500,但是在迭代不到70次时就终止了,这就是Early Stopping在起作用,一定程度上可以避免不必要的训练过程,减少训练时间;过拟合和欠拟合的练习这部分练习可以通过这个notebook完成,这里有通过训练简单线性模型和复杂神经网络模型等,并通过学习曲线来观察模型的拟合情况,并通过添加Early Stopping来控制过拟合情况;Dropout和Batch Normalization实际的神经网络结构中往往包含更多的层,不仅仅是dense层,比如激活层、Dropout层等等,有些类似dense层,定义神经元的连接,而有些则是用于预处理和转换等;DropoutDropout层有助于纠正过拟合问题,在每次训练迭代中,随机的去掉网络层中的一部分输入单元,使得模型难以从训练数据学习到错误的模式,取而代之的是模型会搜索更普遍适用的模式,也就是具有更好的鲁棒性的模式,借此解决过拟合问题;可以把Dropout看作是一种集成方法,与随机森林类似,Dropout的随机抽取类似随机森林的行抽取和列抽取,二者的目的都是解决原始模型的过拟合问题,思路是一样的;增加Dropout在keras中,Drouput作为层使用,作用于其下的一层,通过参数rate指定随机取出的比例:keras.Sequential([ # ... layer.Dropout(rate=0.3), # apply 30% dropout to the next layer layer.Dense(16), # ... ]) Batch Normalization模型在迭代过程中,权重的更新主要由loss和optimater决定,假设我们的输入特征的量纲不一致,比如有的特征范围从0到1,有的特征是从-100到+100,那么在优化器计算过程中就会产生差异很大的结果,并使得训练过程很不稳定,体现就是学习曲线的波动严重;一个小栗子:比如我们要预测房价,目前有两个属性,一个是面积,范围是10到200,另一个是距离火车站距离,范围是100到100000,如果不进行量纲统一,可以遇见的是在计算过程中由于火车站距离值更大,因此会影响对结果的预测,或者说这个范围一定程度上参与了原来权重该起到的作用;Batch Normalization类似SKLearn里的StandardScaler和MinMaxScaler的作用,用于将输入特征的量纲统一,避免因为量纲不同导致对于预测结果影响的权重差异;增加Batch Normalization可以用在某一层之后:layers.Dense(16, activation='relu'), layers.BatchNormalization(), 也可以用在某一层和它的激活层之间:layers.Dense(16), layers.BatchNormalization(), layers.Activation('relu'), 例子 - 使用Dropout和Batch Normalization继续红酒例子,在每一个隐含层后都先加一个Dropout过滤一部分输入解决过拟合,再应用Batch Normalization优化不稳定情况:from tensorflow import keras from tensorflow.keras import layers model = keras.Sequential([ layers.Dense(1024, activation='relu', input_shape=[11]), layers.Dropout(0.3), layers.BatchNormalization(), layers.Dense(1024, activation='relu'), layers.Dropout(0.3), layers.BatchNormalization(), layers.Dense(1024, activation='relu'), layers.Dropout(0.3), layers.BatchNormalization(), layers.Dense(1), ]) 训练过程不使用Early Stopping:model.compile( optimizer='adam', loss='mae', ) history = model.fit( X_train, y_train, validation_data=(X_valid, y_valid), batch_size=256, epochs=100, verbose=0, ) # Show the learning curves history_df = pd.DataFrame(history.history) history_df.loc[:, ['loss', 'val_loss']].plot(); 学习曲线如下:可以看到,首先虽然没有Early Stopping,但是过拟合问题不明显,其次在迭代20次之后不稳定的情况基本消失了,说明Dropout和Batch Normalization都起到了各自的作用;Dropout和Batch Normalization练习这部分练习在这个notebook里,其中分别使用两个数据集,对比其上应用Dropout与不应用,应用Batch Normalization与不应用在学习曲线上的差异,可以很直观的看到二者起到的作用;下面是应用Batch Normalization后的学习曲线,要知道在不应用的情况下曲线都无法绘制出来:分类问题之前处理的都是回归问题,处理分类问题的区别只有以下两点: 损失函数:分类与回归在损失函数应用上不同,比如MAE和准确率; 输出层输出类型:也就是网络结构最后一层输出的内容,之前都是数值,如果是二分类问题,则应该是0/1; Sigmoid函数Sigmoid函数同样作为激活函数,它可以将实数输出映射到0到1之间,也就是通常的概率范围,而不管是准确率还是交叉熵等都可以利用概率来计算得到;Sigmoid函数图像如下,上一个使用它的地方是逻辑回归,同样是将线性回归的结果映射到0和1之间:例子 - 二分类数据格式如下: V1 V2 V3 V4 V5 V6 V7 V8 V9 V10 ... V26 V27 V28 V29 V30 V31 V32 V33 V34 Class 1 0 0.99539 -0.05889 0.85243 0.02306 0.83398 -0.37708 1.00000 0.03760 ... -0.51171 0.41078 -0.46168 0.21266 -0.34090 0.42267 -0.54487 0.18641 -0.45300 good 1 0 1.00000 -0.18829 0.93035 -0.36156 -0.10868 -0.93597 1.00000 -0.04549 ... -0.26569 -0.20468 -0.18401 -0.19040 -0.11593 -0.16626 -0.06288 -0.13738 -0.02447 bad 1 0 1.00000 -0.03365 1.00000 0.00485 1.00000 -0.12062 0.88965 0.01198 ... -0.40220 0.58984 -0.22145 0.43100 -0.17365 0.60436 -0.24180 0.56045 -0.38238 good 1 0 1.00000 -0.45161 1.00000 1.00000 0.71216 -1.00000 0.00000 0.00000 ... 0.90695 0.51613 1.00000 1.00000 -0.20099 0.25682 1.00000 -0.32382 1.00000 bad 1 0 1.00000 -0.02401 0.94140 0.06531 0.92106 -0.23255 0.77152 -0.16399 ... -0.65158 0.13290 -0.53206 0.02431 -0.62197 -0.05707 -0.59573 -0.04608 -0.65697 good 像之前处理回归问题一样定义模型,区别在于最后一层的激活函数选择sigmoid用于输出概率:from tensorflow import keras from tensorflow.keras import layers model = keras.Sequential([ layers.Dense(4, activation='relu', input_shape=[33]), layers.Dense(4, activation='relu'), layers.Dense(1, activation='sigmoid'), ]) 添加交叉熵和准确率到模型中,继续使用adam,他在分类问题上表现依然很好:model.compile( optimizer='adam', loss='binary_crossentropy', metrics=['binary_accuracy'], ) 使用Early Stopping控制训练过程:early_stopping = keras.callbacks.EarlyStopping( patience=10, min_delta=0.001, restore_best_weights=True, ) history = model.fit( X_train, y_train, validation_data=(X_valid, y_valid), batch_size=512, epochs=1000, callbacks=[early_stopping], verbose=0, # hide the output because we have so many epochs ) 分别观察其交叉熵和准确率的变化情况:history_df = pd.DataFrame(history.history) # Start the plot at epoch 5 history_df.loc[5:, ['loss', 'val_loss']].plot() history_df.loc[5:, ['binary_accuracy', 'val_binary_accuracy']].plot() print(("Best Validation Loss: {:0.4f}" +\ "\nBest Validation Accuracy: {:0.4f}")\ .format(history_df['val_loss'].min(), history_df['val_binary_accuracy'].max())) 交叉熵:准确率:分类练习这部分练习在这个notebook,很完整的一个分类模型搭建过程,从基于结构图创建神经网络结构到添加loss和优化器,使用Early Stopping等都有,包括对于结果是否过拟合和欠拟合的讨论等,可以通过这个notebook再次练习下整个深度学习流程,麻雀虽小,五脏俱全;交叉熵:准确率:最后对于深度学习还有很多很多可以学习的内容,本篇文章以最简单的方式对其中各个基础模块进行介绍,并结合代码和运行结果图等进行说明,希望看完能够在脑海中形成对于深度学习的一个感性认识;最后的最后欢迎大佬们关注我的公众号:尼莫的AI小站,新开的公众号,后续会不定期更新有关机器学习、深度学习、数据处理分析、游戏的内容;
2020年10月24日
993 阅读
0 评论
0 点赞
2020-10-24
如何实现自己的任务调度系统?--开源软件诞生18
任务调度与ERP难舍难分--第18篇用日志记录“开源软件”的诞生【点亮星标】----祈盼着一个鼓励博主开源地址:码云:https://gitee.com/redragon/redragon-erpGitHub:https://github.com/redragon1985/redragon-erp 什么是任务调度?任务调度这个词貌似很高大上,其实不难理解。我们知道一个应用它执行任务的方式默认是实时且同步的,而所谓的任务调度就是让任务非实时的或异步的进行。换一个词也可以把它理解成计划任务或定时任务。它解决了什么问题要分析任务调度所解决的问题,先要看这个任务的特点以及需要如何执行。首先这类任务一般是系统根据一定的预设逻辑去代替人工完成的一件事。其次这类事无需实时完成或需要在某个特定时间完成,任务启动后只需在后台静默执行,执行完成后有条件的通知用户或用户自主查询执行结果即可。由此可见,以上需求是传统的应用无法做到的,所以就需要任务调度系统去实现它。为什么单独研发调度系统?从Java技术角度有很多定时任务的实现方式,比如Timer、ScheduledExecutor、Quartz,无论是哪种方式都可以简单的在一个项目中去集成定时任务的功能,而无需单独开发项目。那为什么我们在进行系统设计时,要提出任务调度系统这一思路呢?那就需要先来分析下信息化系统的特点,首先信息化系统从功能设计的角度是分散的,即一个项目或模块一般只完成一件事的情况下,我们从需要设计很多很多的应用才能满足用户需求。就拿ERP举例,ERP包含计划预算、订单管理、库存管理、财务管理等一系列的模块或系统,而这些系统都是与业务直接关联的,所以哪个系统也离不开计划任务的功能,如果按照上述方式,我们必不可少的要将每个项目中都整合一套完整的计划任务组件才能满足我们的需求。这种做法不仅开发时更加麻烦、同时也带来了维护或扩展复杂度的指数提升,这种设计的风险可想而知。所以独立的任务调度系统就应运而生。设计思路探讨那么我们如何设计这个系统呢?我们来重点关注一下几点。1、统一灵活的任务配置(1)任务实现可配置:任务的具体逻辑可以通过方法或存储过程去实现,然后通过配置方式根据不同参数去自定义不同的任务需求。(2)定时执行配置:定义好任务的逻辑后我们就需要配置执行逻辑,什么时间执行,是固定时间还是循环,何时开始何时结束,单次执行还是多次执行都需要详细的配置。(3)线程配置:任务需要同步执行还是异步执行,是守护线程还是非守护线程。异常处理机制等都需要分别配置好。(4)任务流程配置:有的时候我们不仅仅需要执行一个任务,可能需要执行一系列的任务,而任务的执行又是有先后之分的,这时候我们可以将一个一个配置好的任务串联成一个流程去执行。2、便捷的任务启动和反馈(1)启动任务:所有的任务配置好以后我们就需要启动任务,通过API的方式启动是最优的选择。(2)反馈机制:对于任务的执行效果要对用户有一个反馈机制,给用户通知、直接回调或反写都是可以选择的方式。3、监控任务执行情况和日志追踪(1)任务监控:任务的执行过程不是一帆风顺的,可能成功,也可能出现执行异常、意外终止、人工终止等多种情况,所以需要根据任务的执行情况显示执行状态,并根据需要,让用户可重启或继续任务。(2)日志追踪:如果出现重新执行仍然无法成功的情况,那么多数一定是出现了程序异常,所以日志的记录就必不可少,可让维护人员根据日志处理问题并解决问题。抛砖引玉的分布式当一个任务调度系统研发完成后,随着使用频率的增加,一定会遇到瓶颈,所以我们就会考虑到部署多个任务调度系统形成分布式,但部署多个系统,就会涉及到注册中心、负载、路由、数据同步等各种问题。本文只是在讨论如何设计任务调度系统的功能,并不涉及分布式调度的思路。在此不展开谈,只是抛砖引玉,有兴趣的朋友可以深入探索。后记如果您对我们正在做的开源软件感兴趣,欢迎各种形式的合作,作为贡献者或直接加入我们!让我们一起打造一套开源的信息化解决方案。 【码云】或【GitHub】搜索“赤龙ERP”点击星标,亦可加入我们! 让我们从小开始做点伟大的事!与开发者交流 kzca2000
2020年10月24日
891 阅读
0 评论
1 点赞
1
2
...
6
首页
复制
搜索
前进
后退
重载网页
和我当邻居
给我留言吧