「深度学习福利」大神带你进阶工程师,立即查看>>>
简介
这一章节我们讲解马尔科夫模型。给定一组随机变量(如顾客最近的交易日期),马尔科夫模型只根据前一个状态(前一个最近交易日期)的分部指示该变量最近的分布。
1、马尔科夫链基本原理
令 $$ S = {S_1,S_2,...,S_n} $$ 是一个有限的状态集,我们希望得到如下的结果: $$ P(S_n|S_{n-1},S_{n-2},...,S_{1}) \eqsim P(S_n|S_{n-1}) $$ 这个近似公式表明了一阶马尔科夫性质(markov property): 系统在时间t+1的状态只与系统在时间t的状态有关。
那么,如果我们在严谨一点,将这个关联状态再往前面推一个时间点,得到的就是二阶马尔科夫模型: $$ P(S_n|S_{n-1},S_{n-2},...,S_{1}) \eqsim P(S_n|S_{n-1},S_{n-2}) $$
下面使用马尔科夫假设描述联合概率: $$ P(S_1,S_2,...,S_n) = \prod_{i=1}^n P(S_i,S_{i-1}) $$
总结 : 如果一个随机序列的分布仅由其当前状态确定,则具有markov性质。具有这个性质的随机过程称为马尔科夫随机过程(markov random process)。 对于可观察的状态序列(即状态由数据可知),可以得到一个马尔科夫链模型(markov chain model,MCM),我们可以使用这个模型来做一些预测。 对于不可观察状态,会得到一个隐式马尔科夫模型(hidden markov model,HMM)
接下来我们给出将要用到的马尔科夫链的形式化表示
有限的状态空间(state space) $$ S={S_1,S_2,...S_n} $$
转移概率(transition properties)
函数$f:S \times S \to R$:转化结果为一个N阶方阵,其中N是状态的数量,其中: $0 \le f(a,b) \le 1$:即转移概率的值在0到1之间; $\sum_{b \in S} f(a,b) = 1$:即同一个状态转向当前所有可能的状态的概率和为1;
初始分布(initial distribution)
函数$g:S \times R$,其中: $0 \le g(a) \le 1$ $\sum_{a\in S}g(a) = 1$
马尔科夫链是S中的一个随机过程: 时间0时,这个链的初始状态用分布函数$g$创建; 如果时间t时马尔科夫链的状态为$a$,则时间$t+1$时,对于各个$b \in S$,其状态为b的概率为$f(a,b)$
2、马尔科夫状态表的运用
假设我们已经得到了一张马尔科夫状态表。如下表所示的城市天气变化表:
今天天气\明天天气 晴天(sunny) 有雨(rainy) 多云(cloudy) 有雾(foggy)
晴天(sunny)
有雨(rainy) | 多云(cloudy) | 有雾(foggy) 0.6 | 0.5 | 0.1 | 0.0 0.1 | 0.2 | 0.7 | 0.3 0.2 | 0.2 | 0.1 | 0.4 0.1 | 0.1 | 0.1 | 0.3 我们假设: 城市天气只包含四种可能,即晴天(sunny)、有雨(rainy)、多云(cloudy)和有雾(foggy); 一天中天气不会变化; 我们来解释这张表的含义,其实我们完全可以以4阶方阵的形式表示,但为了清晰说明,将其以表格展示。 其中每一个数字代表:当前的天气为XX时,明天的天气为XX的概率。例如,第一行第一列的数据0.6,就代表的是当前天气为晴天时,明天天气为晴天的概率为0.6. 我们就可以用马尔科夫性质的思想回答下面的问题: 如果今天的天气状态是晴天,那么明天是多云而且后天有雾的概率是多大?根据马尔科夫链的思想,明天是多余的概率只与今天有关,后天有雾的概率只与明天有关,因此: P = 0.2(第一行第三列数据) * 0.1(第三行第四列) = 0.02 可见,我们的重点在于由数据推导出马尔科夫状态矩阵的过程。 2、分析客户交易数据 假设我们有这样的一些交易数据: 55FRL8G23B,1381907878,2013-01-09,129 9SX0DJG9L4,1381907879,2013-01-09,34 0ANMD0T113,1381907880,2013-01-09,144 W1F4412PA8,1381907881,2013-01-09,26 22Z10EAYC3,1381907882,2013-01-09,167 3R56N17P1H,1381907883,2013-01-09,25 LK0P6K3DE4,1381907884,2013-01-09,25 3A4S4BMPMU,1381907885,2013-01-09,113 OF4CEO2814,1381907886,2013-01-09,138 9ICNYOFS41,1381907887,2013-01-09,79 N4EOB264U6,1381907888,2013-01-09,108 3C0WARCKYJ,1381907889,2013-01-09,204 PTT3BI00AZ,1381907890,2013-01-09,27 14UHHAVQ2Q,1381907891,2013-01-09,73 Z9GFE6TDKF,1381907892,2013-01-09,32 ... 数据意义为(交易ID,顾客ID,交易日期,交易金额)。 2.1、生成状态序列 这一节的目标是将交易序列(transcation sequence)转化为一个状态序列(state sequence)。我们的应用的目的是为了生成以下的输出 $$ customerId(Date_1,Amount_1);(Date_2,Amount_2),...,(Date_n,Amount_n) $$ 其中日期按增序排序。 我们要把这个输出转化为一个状态序列,如下所示: $$ customerId,State_1,State_2,...,State_n $$ 我们还需要将(purchase-date,amount)对的有序序列转换为一组马尔科夫链状态。状态由一个代号(两个字母)表示,各个字母的定义如下所示: 上一次交易后经过的时间 与前次交易相比的交易额 | S:小 | L:显著小于 M:中 | L:大 E:基本相同 | G:显著大于 因此,我们可以得到9种状态 状态名 上一次交易后的经过时间与其次交易相比的交易额 | SL | 小:显著小于 SE | 小:基本相同 |
---|
SG | 小:显著大于 | ML | 中:显著小于 | ME | 中:基本相同 | MG | 中:显著大于 | LL | 大:显著小于 LE | LG 大:基本相同 | 大:显著大于 | 可以看到,我们的马尔科夫链模型有9个状态(9*9矩阵)。 转换过程可以使用一些小脚本实现,但是如果数据量过大,还是需要一个map过程。 2.2、使用MapReduce生成马尔科夫状态转移矩阵 3、Spark实现 3.1、主类 下面是spark的主任务类。下一章节会列出所使用到的辅助类。 package com.sunrun.movieshow.algorithm.markov; import com.sunrun.movieshow.algorithm.common.SparkHelper; import org.apache.spark.api.java.JavaPairRDD; import org.apache.spark.api.java.JavaRDD; import org.apache.spark.api.java.JavaSparkContext; import scala.Tuple2; import java.io.Serializable; import java.util.*; public class SparkMarkov { public static void main(String[] args) { JavaSparkContext sc = SparkHelper.getSparkContext("markov"); // 1.归约到不同的分区 JavaRDD rdd = sc.textFile("data/markov").coalesce(8); // 2.得到(顾客ID,(交易时间,交易额)) JavaPairRDD> pairRDD = rdd.mapToPair(line -> { // tokens[0] = customer-id // tokens[1] = transaction-id // tokens[2] = purchase-date // tokens[3] = amount String[] tokens = line.split(","); if (tokens.length != 4) { return null; } else { long date = 0; try { date = DateUtil.getDateAsMilliSeconds(tokens[2]); } catch(Exception e) { // 会有异常数据 } int amount = Integer.parseInt(tokens[3]); Tuple2 V = new Tuple2<>(date, amount); return new Tuple2<>(tokens[0], V); } }); /** * (V31E55G4FI,(1356969600000,123)) * (301UNH7I2F,(1356969600000,148)) * (PP2KVIR4LD,(1356969600000,163)) * (AC57MM3WNV,(1356969600000,188)) * ... */ // 4.依据交易ID进行分组 - 用户信息在最后的状态链中是没用的,但他可以限定一些状态切换的内在关系, // 这在很多算法过程中都是隐含条件,需要经验判断。 JavaPairRDD>> customerRDD = pairRDD.groupByKey(); /** * (V31E55G4FI,(1356969600000,123),1356969600000,148)....) * ... */ // 5.创建马尔科夫状态序列 (c_id,<(time1,amount),(time2,amount)>) => (String) JavaPairRDD> stateSequence = customerRDD.mapValues((Iterable> dateAndAmount) -> { List> list = toList(dateAndAmount); Collections.sort(list, TupleComparatorAscending.INSTANCE); // now convert sorted list (be date) into a "state sequence" List stateSequence1 = toStateSequence(list); return stateSequence1; }); /** * stateSequence.saveAsTextFile("out/" + UUID.randomUUID()); * 顾客id,与其相关的状态序列 * (K40E0LA5DL,[LL, MG, SG, SL, SG, MG, SL, SG, SL, SG, LL]) * (ICF0KFGK12,[SG, SL, SE, SG, LL, LG]) * (4N0B1U5HVG,[SG, ML, MG, SG, SL, SG, SL, SG, ML]) * (3KJR1907D9,[SG, SL, ML, SG, ML, LG]) * (47620I9LOD,[LG, SL, ML, MG, SG, SL, SG, SL, SG]) */ // 6.接下来,我们将状态序列以窗口为2的方式依次移动,生成一个个形如((LL, MG),1)的对 JavaPairRDD, Integer> model = stateSequence.flatMapToPair(s -> { // 输出形式((fromState,toState),times) ArrayList, Integer>> mapperOutput = new ArrayList<>(); List states = s._2; if (states == null) { return Collections.emptyIterator(); } else { for (int i = 0; i < states.size() - 1; i++) { String fromState = states.get(i); String toState = states.get(i + 1); Tuple2 k = new Tuple2<>(fromState, toState); mapperOutput.add(new Tuple2<>(k, 1)); } } return mapperOutput.iterator(); }); /** * model.saveAsTextFile("out/model"); * ((LG,LL),1) * ((LL,MG),1) * ((MG,SG),1) * ((SG,SL),1) * ((SL,ME),1) * ((ME,MG),1) * */ // 7.我们需要将这些结果组合归约,将相同的状态序列进行合并,即从(f,t),1) => ((f,t),3000)的形式。 JavaPairRDD, Integer> markovModel = model.reduceByKey((a, b) -> a + b); /** * markovModel.saveAsTextFile("/out/markov"); * ((LL,SL),993) * ((MG,LL),1859) * ((LE,ME),25) * ((SL,ME),1490) * ((LG,ME),153) * ((ML,LG),3991) * ((ME,ME),58) */ // 8.我们格式化一下输出,将其形式转变为(f,t,times)的形式 JavaRDD markovFormatModel = markovModel.map(t -> t._1._1 + "\t" + t._1._2 + "\t" + t._2); // 9.将结果存储到HDFS服务器,当然也可以存储到本地,这时候解析类就要使用本地文件系统的API String markovFormatModelStorePath = "hdfs://10.21.1.24:9000/markov/"; markovFormatModel.saveAsTextFile(markovFormatModelStorePath); // 9.将最终结果进行转换,生成马尔科夫概率模型 MarkovTableBuilder.transport(markovFormatModelStorePath); /** 这是一个state阶的方阵,A(ij)表示状态i到状态j的转化概率 * 0.03318,0.02441,0.6608,0.1373,0.007937,0.02398,0.06456,0.009522,0.03832 * 0.3842,0.02532,0.2709,0.1260,0.009590,0.01331,0.1251,0.01083,0.03477 * 0.6403,0.02881,0.05017,0.1487,0.01338,0.008602,0.08359,0.01446,0.01196 * 0.02081,0.002368,0.7035,0.01170,0.004638,0.1518,0.03901,0.005933,0.06030 * 0.4309,0.02140,0.2657,0.1277,0.009697,0.02006,0.08159,0.009530,0.03344 * 0.1847,0.01816,0.5316,0.1303,0.007175,0.02351,0.06265,0.008136,0.03379 * 0.01847,0.004597,0.6115,0.02068,0.003091,0.1346,0.06828,0.01424,0.1245 * 0.2250,0.01900,0.2427,0.06291,0.006335,0.03182,0.2757,0.03801,0.09847 * 0.2819,0.01944,0.1954,0.07990,0.008015,0.03202,0.2612,0.04427,0.07781 */ } // 将迭代器转化为数组 static List> toList(Iterable> iterable) { List> list = new ArrayList>(); for (Tuple2 element: iterable) { list.add(element); } return list; } // 按时间进行排序 static class TupleComparatorAscending implements Comparator>, Serializable { final static TupleComparatorAscending INSTANCE = new TupleComparatorAscending(); @Override public int compare(Tuple2 t1, Tuple2 t2) { // return -t1._1.compareTo(t2._1); // sorts RDD elements descending return t1._1.compareTo(t2._1); // sorts RDD elements ascending } } // 将一个有序的交易序列(List>)转化为状态序列(List),其中各个元素分别表示一个马尔科夫状态。 static List toStateSequence(List> list){ // 没有足够的数据 if(list.size() < 2){ return null; } List stateSequence = new ArrayList<>(); // == 两两配对计算结果 Tuple2 prior = list.get(0); for (int i = 1; i < list.size(); i++) { Tuple2 current = list.get(i); // === 计算时间差(天),由于数据是以ms计数的,因此需要转化为天(1d = 24*60*60*1000=86400000ms) long dayDiff = (current._1 - prior._1) / 86400000; // === 获取交易额信息 int priorAmount = prior._2; int currentAmount = current._2; // === 根据业务规则转化为字母表示 // ==== 处理时间关系 String dd = null; if(dayDiff < 30){ dd = "S"; }else if(dayDiff < 60){ dd = "M"; }else { dd = "L"; } // ==== 处理金额关系: 使用两次交易额的比重 String ad = null; if(priorAmount < 0.9 * currentAmount){ ad = "L"; }else if(priorAmount < 1.1 * currentAmount){ ad = "E"; }else{ ad = "G"; } // === 组合结果 String element = dd + ad; stateSequence.add(element); // 大小为2的窗口前进一格 prior = current; } return stateSequence; } } 3.2、辅助类 日期处理类 package com.sunrun.movieshow.algorithm.markov; import java.text.SimpleDateFormat; import java.util.Date; public class DateUtil { static final String DATE_FORMAT = "yyyy-MM-dd"; static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat(DATE_FORMAT); /** * Returns the Date from a given dateAsString */ public static Date getDate(String dateAsString) { try { return SIMPLE_DATE_FORMAT.parse(dateAsString); } catch(Exception e) { return null; } } /** * Returns the number of milliseconds since January 1, 1970, * 00:00:00 GMT represented by this Date object. */ public static long getDateAsMilliSeconds(Date date) throws Exception { return date.getTime(); } /** * Returns the number of milliseconds since January 1, 1970, * 00:00:00 GMT represented by this Date object. */ public static long getDateAsMilliSeconds(String dateAsString) throws Exception { Date date = getDate(dateAsString); return date.getTime(); } public static String getDateAsString(long timestamp) { return SIMPLE_DATE_FORMAT.format(timestamp); } } 2、输入输出工具类 package com.sunrun.movieshow.algorithm.markov; import java.io.InputStream; import java.io.OutputStream; import java.io.BufferedReader; // import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.util.LineReader; /** * This class provides convenient methods for accessing * some Input/Output methods. * * @author Mahmoud Parsian (mahmoud.parsian@yahoo.com) * */ public class InputOutputUtil { public static void close(LineReader reader) { if (reader == null) { return; } // try { reader.close(); } catch (Exception ignore) { } } public static void close(OutputStream stream) { if (stream == null) { return; } // try { stream.close(); } catch (Exception ignore) { } } public static void close(InputStream stream) { if (stream == null) { return; } // try { stream.close(); } catch (Exception ignore) { } } public static void close(FSDataInputStream stream) { if (stream == null) { return; } // try { stream.close(); } catch (Exception ignore) { } } public static void close(BufferedReader reader) { if (reader == null) { return; } // try { reader.close(); } catch (Exception ignore) { } } } 3、马尔科夫状态处理类 package com.sunrun.movieshow.algorithm.markov; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 输入数据生成马尔科夫状态表 */ public class MarkovTableBuilder { // 状态的数量 private int numberOfState; private int scale; // 状态表 private double[][] table = null; // 状态序列 private Map states = null; public MarkovTableBuilder(int numberOfState, int scale){ this(numberOfState); this.scale = scale; } private MarkovTableBuilder(int numberOfState){ this.numberOfState = numberOfState; table = new double[numberOfState][numberOfState]; initStates(); } // 初始化state状态 private void initStates(){ states = new HashMap(); states.put("SL", 0); states.put("SE", 1); states.put("SG", 2); states.put("ML", 3); states.put("ME", 4); states.put("MG", 5); states.put("LL", 6); states.put("LE", 7); states.put("LG", 8); } // 将状态信息添加到状态表 public void add(StateItem item){ // 获取该状态对应的角标 int row = states.get(item.fromState); int column = states.get(item.toState); table[row][column] = item.count; } public void normalize() { // // 拉普拉斯校正:一般通过将所有的计数+1来进行。see: http://cs.nyu.edu/faculty/davise/ai/bayesText.html for (int r = 0; r < numberOfState; r++) { boolean gotZeroCount = false; for (int c = 0; c < numberOfState; c++) { if(table[r][c] == 0) { gotZeroCount = true; break; } } if (gotZeroCount) { for (int c = 0; c < numberOfState; c++) { table[r][c] += 1; } } } // normalize for (int r = 0; r < numberOfState; r++) { double rowSum = getRowSum(r); for (int c = 0; c < numberOfState; c++) { table[r][c] = table[r][c] / rowSum; } } } // 获取rowNumber行的累加结果 public double getRowSum(int rowNumber) { double sum = 0.0; for (int column = 0; column < numberOfState; column++) { sum += table[rowNumber][column]; } return sum; } /** * 存储状态表:作为示例,只是将结果输出即可。 */ private void persist() { for (int row = 0; row < numberOfState; row++) { StringBuilder builder = new StringBuilder(); for (int column = 0; column < numberOfState; column++) { double element = table[row][column]; builder.append(String.format("%.4g", element)); if (column < (numberOfState - 1)) { builder.append(","); } } System.out.println(builder.toString()); } } public static void transport(String markovFormatDataPath){ List items = ReadDataFromHDFS.readDirectory(markovFormatDataPath); MarkovTableBuilder markovTableBuilder = new MarkovTableBuilder(9); for (StateItem item : items) { markovTableBuilder.add(item); } // 归一化数据 markovTableBuilder.normalize(); // 存储马尔科夫状态表 markovTableBuilder.persist(); } } 4、从文件系统读取数据的处理类 package com.sunrun.movieshow.algorithm.markov; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FSDataInputStream; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.log4j.Logger; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; /** * Class containing a number of utility methods for manipulating * Hadoop's SequenceFiles. * * * @author Mahmoud Parsian * */ public class ReadDataFromHDFS { private ReadDataFromHDFS() { } public static List readDirectory(String path) { return ReadDataFromHDFS.readDirectory(new Path(path)); } public static List readDirectory(Path path) { FileSystem fs; try { fs = path.getFileSystem(new Configuration()); } catch (IOException e) { System.out.println("Unable to access the hadoop file system!"); throw new RuntimeException("Unable to access the hadoop file system!"); } List list = new ArrayList(); try { FileStatus[] stat = fs.listStatus(path); for (int i = 0; i < stat.length; ++i) { if (stat[i].getPath().getName().startsWith("part")) { List pairs = readFile(stat[i].getPath(), fs); list.addAll(pairs); } } } catch (IOException e) { System.out.println("Unable to access the hadoop file system!"); throw new RuntimeException("Error reading the hadoop file system!"); } return list; } @SuppressWarnings("unchecked") public static List readFile(Path path, FileSystem fs) { List list = new ArrayList(); FSDataInputStream stream = null; BufferedReader reader = null; try { stream = fs.open(path); reader = new BufferedReader(new InputStreamReader(stream)); String line; while ((line = reader.readLine()) != null) { // line = <,> String[] tokens = line.split("\t"); // TAB separator if (tokens.length == 3) { StateItem item = new StateItem(tokens[0], tokens[1], Integer.parseInt(tokens[2])); list.add(item); } } } catch (IOException e) { System.out.println("readFileIntoCoxRegressionItem() failed!"); throw new RuntimeException("readFileIntoCoxRegressionItem() failed!"); } finally { InputOutputUtil.close(reader); InputOutputUtil.close(stream); } return list; } public static void main(String[] args) throws Exception { String path = "hdfs://10.21.1.24:9000/markov/"; List list = readDirectory(path); System.out.println("list="+list.toString()); } } 5、马尔科夫状态实体类 package com.sunrun.movieshow.algorithm.markov; /** * TableItem represents an item of a Markov State Transition Model * as a Tuple3 * */ public class StateItem { String fromState; String toState; int count; public StateItem(String fromState, String toState, int count) { this.fromState = fromState; this.toState = toState; this.count = count; } /** * for debugging ONLY */ public String toString() { return "{"+fromState+"," +toState+","+count+"}"; } } 4、使用最终的马尔科夫表进行预测 前面我已经在代码注释中拿出了最后的分析结果: SL SE SG ML ME MG LL LE LG SL | SE | SG | ML | ME | MG | LL | LE | LG 0.03318 | 0.3842 | 0.6403 | 0.02081 | 0.4309 | 0.1847 | 0.01847 | 0.2250 | 0.2819 0.02441 | 0.02532 | 0.02881 | 0.002368 | 0.02140 | 0.01816 | 0.004597 | 0.01900 | 0.01944 0.6608 | 0.2709 | 0.05017 | 0.7035 | 0.2657 | 0.5316 | 0.6115 | 0.2427 | 0.1954 0.1373 | 0.1260 | 0.1487 | 0.01170 | 0.1277 | 0.1303 | 0.02068 | 0.06291 | 0.07990 0.007937 | 0.009590 | 0.01338 | 0.004638 | 0.009697 | 0.007175 | 0.003091 | 0.006335 | 0.008015 0.02398 | 0.01331 | 0.008602 | 0.1518 | 0.02006 | 0.02351 | 0.1346 | 0.03182 | 0.03202 0.06456 | 0.1251 | 0.08359 | 0.03901 | 0.08159 | 0.06265 | 0.06828 | 0.2757 | 0.2612 0.009522 | 0.01083 | 0.01446 | 0.005933 | 0.009530 | 0.008136 | 0.01424 | 0.03801 | 0.04427 0.03832 | 0.03477 | 0.01196 | 0.06030 | 0.03344 | 0.03379 | 0.1245 | 0.09847 | 0.07781 参考 数据算法 —— Mahmoud Parsian(很喜欢该作者写的这本书,非常详细) 附录 1、ruby脚本 mark_plan.rb #!/usr/bin/ruby require '../lib/util.rb' require 'Date' xaction_file = ARGV[0] model_file = ARGV[1] userXactions = {} model = [] states = ["SL", "SE", "SG", "ML", "ME", "MG", "LL", "LE", "LG"] # read all xactions File.open(xaction_file, "r").each_line do |line| items = line.split(",") custID = items[0] if (userXactions.key? custID) hist = userXactions[custID] else hist = [] userXactions[custID] = hist end hist << items[2, items.size - 2] end #read model File.open(model_file, "r").each_line do |line| items = line.split(",") #puts "#{line}" row = [] items.each do |item| row << item.to_i #puts "#{item}" end model << row #puts "#{row}" end # marketing time userXactions.each do |cid, xactions| seq = [] last_date = Date.parse "2000-01-01" xactions.each_with_index do |xaction, index| if (index > 0) prevXaction = xactions[index-1] prDate = Date.parse prevXaction[0] prAmt = prevXaction[1].to_i date = Date.parse xaction[0] last_date = date amt = xaction[1].to_i daysDiff = date - prDate amtDiff = amt - prAmt if (daysDiff < 30) dd = "S" elsif (daysDiff < 60) dd = "M" else dd = "L" end if (prAmt < 0.9 * amt) ad = "L" elsif (prAmt < 1.1 * amt) ad = "E" else ad = "G" end seq << (dd + ad) end end if (!seq.empty?) last = seq[-1] row_index = states.index(last) row = model[row_index] max_col = row.max col_index = row.index(max_col) next_state = states[col_index] if (next_state.start_with?("S")) next_date = last_date + 15 elsif (next_state.start_with?("M")) next_date = last_date + 45 else next_date = last_date + 90 end #puts "#{cid}, #{last}, #{row_index}, #{max_col}, #{col_index}, #{next_state}, #{last_date}, #{next_date}" puts "#{cid}, #{next_date}" end end buy_xaction.rb #!/usr/bin/ruby require '../lib/util.rb' require 'Date' custCount = ARGV[0].to_i daysCount = ARGV[1].to_i visitorPercent = ARGV[2].to_f custIDs = [] xactionHist = {} # transition probability matrix idGen = IdGenerator.new 1.upto custCount do custIDs << idGen.generate(10) end xid = Time.now().to_i date = Date.parse "2013-01-01" 1.upto daysCount do numXaction = visitorPercent * custCount factor = 85 + rand(30) numXaction = (numXaction * factor) / 100 1.upto numXaction do custID = custIDs[rand(custIDs.size)] if (xactionHist.key? custID) hist = xactionHist[custID] lastXaction = hist[-1] lastDate = lastXaction[0] lastAmt = lastXaction[1] numDays = date - lastDate if (numDays < 30) amount = lastAmt < 40 ? 50 + rand(20) - 10 : 30 + rand(10) - 5 elsif (numDays < 60) amount = lastAmt < 80 ? 100 + rand(40) - 20 : 60 + rand(20) - 10 else amount = lastAmt < 150 ? 180 + rand(60) - 30 : 120 + rand(40) - 20 end else hist = [] xactionHist[custID] = hist amount = 40 + rand(180) end xaction = [] xaction << date xaction << amount hist << xaction xid = xid + 1 puts "#{custID},#{xid},#{date},#{amount}" end date = date.next end 「深度学习福利」大神带你进阶工程师,立即查看>>> 前几天的3.15晚会上曝光了利用智能机器人,一天打4万个骚扰电话,从而赚取利润的黑色产业链。 阿里的工程师恼了,技术是用来让人们生活变美好的,不是被利用来走向阴暗的。 机器人的问题交给机器人! 工程师们用业余时间开发的AI机器人——“二哈”要登场了~ 视频感受一下咱们“二哈”,连女推销员都丝毫没有察觉。 1、 别惹程序员,用机器人对付机器人 研发“二哈”,其实就是阿里的技术人,对日益普遍的骚扰电话很头疼,而且更可气的是,大量骚扰电话是机器自动拨出的,成本越来越低。 这些骚扰电话,甚至还影响到日常工作,会议开着开着,经常被各类电话打断导致会议终中断。于是阿里AI实验室的同学们开始思考着如何用技术的方式解决问题。 阿里AI实验室天猫精灵算法团队,聚集着语音识别、NLP、知识图谱……全链条的技术人才,当技术人聚在一起,那就能搞事情了。 于是他们先以“课余”项目开始,参与的同学都很开心。一方面是解气,另一方面更清楚背后带来的意义。 没想到在今年3.15晚会上,机器人骚扰电话引起如此大关注。 于是团队决定公开最新研发成果,“二哈”也正式从幕后走到台前,一炮而红。 2、 我们要做到真假难辨 “二哈”是这个智能防骚扰电话技术的项目代号。当用户在手机上开通了这项服务,在接到骚扰来电时,用户可以直接转接给机器人接听。 我们都知道哈士奇之所以被喊作“二哈”,就是它欢乐多,工作起来又相当认真。阿里的程序员们,就是希望这个“课余”爱好的项目,给自己带来快乐,也能帮主人扫除骚扰电话的焦虑。 而“二哈”背后,其实主要使用了智能聊天技术。 所谓“智能聊天”,就是要理解用户请求,同时用强大的知识图谱制作聊天的知识点,通过跟用户对话,把知识图谱里的知识灌输给用户,同时也引导用户反馈。 听起来,简单的一段话,要真正实现聊天聊得“真假难辨”却十分不容易。 于是,我们特地请教了阿里达摩院AI实验室语音助手首席科学家聂再清。 他告诉大家,要做到真假难辨,最核心的是以下3点: 第一,接住用户的每一句话。 因为用户的输入空间非常大,要理解和聪明的回复来电者的每一句话很有难度。 对于一些不是“二哈”知识领域的问题,阿里AI目前采用了闲聊(ChitChat)的技术,从互联网上公开的人类对话库中来找到最适合回复。目标是让来电者看不出“二哈”可能的知识缺乏。 第二,主动提出一个相关问题。 这更多是一个对话策略学习的工作,需要让来电者知道“二哈”真能理解他们的话,并提出只有理解了对话上下文,才能提出的好问题。 一味被动接话,很容易被识破。 该项挑战非常大,核心就是一套聊天机器人对话策略学习框架,让“二哈”这样的聊天机器人在不同的场景下都可以持续学习。 通过和人类不断对话,“二哈”AI会通过深度强化学习越来越好地知道如何提问才能更好地达成他的目标。 最后,像真人一样的语音语调以及停顿。 如果推销广告的骚扰电话,通过声音判断初接电话的是个机器人,他们就不会继续聊下去了。 所以“二哈”在语音合成方面做了很多工作。 阿里AI最新算法利用Tacotron加上基于神经网络的声码器算法,合成出极其逼真的人声,但还有很大比例情况不能通过图灵测试。于是“二哈”现在采取了过渡性方案,把非常高频的回复借用真人录播。 感受一下“二哈”真声和“二哈”的互相调戏学习,竟然没有一点违和感呢。 3、 不止用来对付骚扰电话 “二哈”就只是帮对付骚扰电话而已嘛? 当然不是咯。 项目组的工程师说,“二哈”的进一步的目标是成为每个人的AI电话秘书,除了帮主人应付骚扰电话,还可以在主人繁忙的时候询问来电的主要意图,也能帮主人预约议程。 在你不方便接电话时帮接听来电,并转换成文字向你“汇报”。 “二哈”这样的AI电话秘书,可以利用个性化TTS,模仿出跟主人声音极尽逼真的声音。可以把它看作是你的‘声替’,也可以用任何喜欢的声音替你接打电话。 阿里工程师还在畅想,是否有一天,它还还可以帮助聋哑人打电话?技术的进步可以延伸人的能力,也可以弥补人的缺陷。 阿里人常说,所有的异想天开,终究化为寻常。希望这样的“寻常”能早点到来! 讲真,这样的私人秘书,你期不期待? 最后告诉你一个小技能:在支付宝搜索天猫精灵,或直接打开天猫精灵App,就可以调戏这只二哈! 原文链接 「深度学习福利」大神带你进阶工程师,立即查看>>> 新发布1.7.0版本的hanlp自然语言处理工具包差不多已经有半年时间了,最近也是一直在整理这个新版本hanlp分词工具的相关内容。不过按照当前的整理进度,还需要一段时间再给大家详细分享整理的内容。昨天正好看到的这篇关于关于1.7.0版本hanlp分词在spark中的使用介绍的文章,顺便分享给大家一起学习一下! 以下为分享的文章内容: HanLP分词,如README中所说,如果没有特殊需求,可以通过maven配置,如果要添加自定义词典,需要下载“依赖jar包和用户字典". 直接"java xf hanlp-1.6.8-sources.jar" 解压源码,把源码加入工程(依赖本地jar包,有些麻烦,有时候到服务器有找不到jar包的情况) 按照文档操作,在Spark中分词,默认找的是本地目录,所以如果是在driver中分词是没有问题的。但是如果要分布式分词,是要把词典目录放在HDFS上面,因为这样每台机器才可以访问到 【参考代码】 最好把新增词典放在首位,第一次使用时,HanLP会把新增txt文件,生成bin文件,这个过程比较慢。但是只需要跑一次,它会把bin文件写到HDFS路径上面,第二次以后速度就快一些了。 注意到issue中说,只可以在mapPartition中使用。 参考scala代码 class HadoopFileIoAdapter extends IIOAdapter { override def create(path: String): java.io.OutputStream = { val conf: Configuration = new Configuration() val fs: FileSystem = FileSystem.get(URI.create(path), conf) fs.create(new Path(path)) } override def open(path: String): java.io.InputStream = { val conf: Configuration = new Configuration() val fs: FileSystem = FileSystem.get(URI.create(path), conf) fs.open(new Path(path)) } } def myfuncPerPartition_ ( iter : Iterator [String] ) : Iterator[(Int, mutable.Buffer[String])] = { println("run in partition") val keyWordNum = 6 HanLP.Config.IOAdapter = new HadoopFileIoAdapter val ret = iter.filter(_.split(",",2).length==2) .map(line=>(line.split(",",2)(1).trim.hashCode, HanLP.extractKeyword(line.split(",",2)(0),keyWordNum) .map(str=>str.filterNot(stopChar.contains(_))).filter(w=>(w.length>1 || ( w.length==1 && white_single_word.contains(w(0))) )) .filterNot(stopWords.contains(_)).take(keyWordNum).distinct)) ret } //调用 raw_data.repartition(100).mapPartitions(myfuncPerPartition_) --------------------- 「深度学习福利」大神带你进阶工程师,立即查看>>> 前言 只有光头才能变强。 文本已收录至我的GitHub仓库,欢迎Star: https://github.com/ZhongFuCheng3y/3y 最近在学习TensorFlow的相关知识,了解了TensorFlow一些基础的知识,现在周末有空了,就写写一些笔记,记录一下自己的成长~ 总的来说,TensorFlow还是一个比较新的技术,有兴趣的同学不妨跟着我的笔记,一起学学呗(反正没坏处)~ 前面回顾: 外行人都能看得懂的机器学习,错过了血亏! 这是我看过最好的「机器学习」科普文章了 神经网络浅讲:从神经元到深度学习 知识背景:没有Python基础,没有机器学习基础,可以说是真正从零学TensorFlow的了。 一、安装Tensorflow所需要的环境 1.1安装Python环境 使用环境: Mac Python有好多个版本,在Mac中自带的Python版本是 2.7 ,但相对而言比较老了。现在一般用 Python3.x 了,本次下载的版本是 3.5.6 我安装Python所使用方法的大致步骤: 安装Homebrew 安装pyenv 在 .bash_profile 添加环境变量 让环境变量生效 安装 3.X版本python 详情可参考如下的链接(总的来说,我根据下面的教程一下子就安装了我想要的Python版本,还是非常不错的。): Mac下安装多版本python: https://www.cnblogs.com/webgiser/p/7463974.html pyenv相关的命令(常用的pyenv命令): https://www.jianshu.com/p/af1f8d7b6b31 在安装Python的时候也发现了一个小知识点: Mac一般使用bash作为默认的shell,会有好几个环境变量,其中 etc/profile 是 系统级 的,还有几个 用户级别 的环境变量,比如说: ~/.bash_profile 如果修改的是系统级别的环境变量,也就是 etc/profile ,在保存的时候只能是 :wq !覆盖,不然会出现readonly错误 (至于TensorFlow和numpy依赖的下载,这里我就不说了,就两条命令就搞掂啦) 1.2PyCharm编辑器 Java有 IDEA 神器,同样的,Python也有 PyCharm ,同样是JetBrains出产的。 我在之前从来没使用过PyCharm,也没写过一句的Python代码。 把Pycharm安装好了之后,我想当然就 new project 去用了,然后就 next,next,next ,当成IDEA来用。但发现了一个问题:我在之前 明明装好了 TensorFlow和numpy的依赖,在PyCharm环境下却 识别不出来 !在 命令行 窗口下,依赖是存在的! 后来才发现:如果用PyCharm去new一个project,会 默认建一个解析器 在当前的项目目录下。而PyCharm会遵守“就近原则”使用当前项目路径下的解析器。而我们添加的依赖(TensorFlow、numpy)是在我们安装Python的路径下的,这就导致了在命令行下找到依赖,而在PyCharm中找不到依赖。 解决也很简单:在PyCharm 指定 一下我们安装Python的路径,切换一下就好了。 参考资料: 关于pip安装第三方库,但pycharm中却无法识别的问题;以及pycharm安装第三方库的方法解析 https://blog.csdn.net/weixin_41287260/article/details/83957731 二、体验TensorFlow 好的,我们现在已经安装好TensorFlow所需要的环境了!(看起来好简单,但还是花了我不少时间....) 官网的Demo链接(需要科学上网): https://www.tensorflow.org/tutorials/keras/basic_classification 首先我们来看一下这个例子可以干些什么:我们有非常多张的图片( 数据集 ),这些图片有不同的类型(比如衣服、鞋子、裤子, 一共有10种类型 ),把这些数据集扔进我们的神经网络里头,生成出 模型 。有了模型以后,当我们再将类似的图片扔进去,这个模型可以帮我们 预测 这张图片是衣服还是鞋子还是裤子。 随后,我跟着官网的代码跑了一(代码我都只是一步一步 复制粘贴 ),最后跑起来: # TensorFlow and tf.keras import tensorflow as tf from tensorflow import keras # Helper libraries import numpy as np print(tf.__version__) # 加载数据、区分出测试数据和训练数据 # 注意:如果已经下过,重复下载的话,可能会出现EOFError: Compressed file ended before the end-of-stream marker was reached错误 fashion_mnist = keras.datasets.fashion_mnist (train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data() # 分类的列表 class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot'] # 查看数据的值 print(train_images.shape) # 样本的shape(可以看出有多少条样本、维度) print(len(train_labels)) # label的个数,有多少个样本,就应该有多少个label print(train_labels) # label的值,对应上面分类列表(从0到9) # 对测试数据和训练数据进行预处理(实际上就是归一化) train_images = train_images / 255.0 test_images = test_images / 255.0 # 设置层 (初始处理)--- 建立神经层 model = keras.Sequential([ keras.layers.Flatten(input_shape=(28, 28)), keras.layers.Dense(128, activation=tf.nn.relu), keras.layers.Dense(10, activation=tf.nn.softmax) ]) # 损失函数、优化器、指标 model.compile(optimizer=tf.train.AdamOptimizer(), loss='sparse_categorical_crossentropy', metrics=['accuracy']) # 将训练集丢进去,训练出模型(Model) model.fit(train_images, train_labels, epochs=5) # 将测试数据丢到模型中,评估一下得分(准确率) test_loss, test_acc = model.evaluate(test_images, test_labels) print('Test accuracy:', test_acc) # 评估完准确率以后,我们可以对测试数据进行预测 predictions = model.predict(test_images) # 选第一个样本预测后的得出最有可能结果 print(np.argmax(predictions[0])) # 对比结果 print(test_labels[0]) 跑出来的结果: (后期注释:这是TensorFlow的版本) 1.12.0 (后期注释:这是TensorFlow下载数据集的日志) Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz 32768/29515 [=================================] - 0s 3us/step Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz 26427392/26421880 [==============================] - 12s 0us/step Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz 8192/5148 [===============================================] - 0s 0us/step Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz 4423680/4422102 [==============================] - 2s 0us/step (后期注释:样本的shape) (60000, 28, 28) (后期注释:样本有多少条,对应的label就有多少条) 60000 (后期注释:label的值是从0-9,表示有九种类别) [9 0 0 ... 3 0 5] (后期注释:开始训练) Epoch 1/5 2019-02-19 14:00:46.842852: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA 60000/60000 [==============================] - 4s 65us/step - loss: 0.4995 - acc: 0.8246 Epoch 2/5 60000/60000 [==============================] - 4s 62us/step - loss: 0.3734 - acc: 0.8655 Epoch 3/5 60000/60000 [==============================] - 4s 63us/step - loss: 0.3346 - acc: 0.8788 Epoch 4/5 60000/60000 [==============================] - 4s 63us/step - loss: 0.3102 - acc: 0.8861 Epoch 5/5 60000/60000 [==============================] - 4s 63us/step - loss: 0.2918 - acc: 0.8916 10000/10000 [==============================] - 0s 36us/step (后期注释:预测出来的精确度) Test accuracy: 0.8514 (后期注释:选第一个样本进行预测,并对比实际的结果) 9 9 参考资料: TensorFlow官网的例子(需要科学上网才能进..) Tensorflow学习笔记之一:训练你的第一个神经网络——基础分类 https://www.jianshu.com/p/d4fb3a391d22 2.1总结一下使用TensorFlow的步骤 加载 数据:使用dataset的api加载数据,并将数据集 分成 训练数据和测试数据 检查 数据:检查 dataSet 的数据有没有问题(例如,样本的记录数、label的记录数等) 对数据 预处理 :对测试数据和训练数据进行 归一化 处理,目的:减少因为数值的大小所带来的影响( 一般我们会将值都缩小在一个小的范围内 ) 建立神经网络 :(输入层、隐藏层、输出层) 为模型定义损失函数、优化器、指标 将训练数据丢进我们的神经网络中,生成出Model 将测试数据丢进我们生成好的Model进行 评估 ,预测出我们的准确率 三、TensorFlow介绍 3.1什么是TensorFlow 如果去Google搜关键字“TensorFlow”,那可能你会看到这么一句话: An open source machine learning framework for everyone. 没错,TensorFlow就是一个机器学习的 框架 。至于框架,相信大家也比较好理解了;类比到Java,比如说我们的Spring框架给我们封装了好多好用的API,简化我们的开发(想想当年写Servlet的时候!) TensorFlow作为机器学习的框架,同样也给我们封装了好多好用的API,能够 降低 学习机器学习的 门槛 。 就拿上面的例子来说,没想到 那么少的代码 就可以跑机器学习的“HelloWorld”了! 至于机器学习和神经网络的相关基础,我 强烈 建议读读这两篇文章!通俗易懂的 科普 文章: 这是我看过最好的「机器学习」科普文章了 神经网络浅讲:从神经元到深度学习 3.2TensorFlow的架构 我们可以发现上面的Demo所用的基础语言是Python,但不要认为TensorFlow就是Python的一个框架。我看到过一个比喻,觉得写得挺好的,粘贴一下: Python 和 Tensorflow 之间的联系,可以类比 Javascript 和 HTML 之间的关系。Javascript 是一种全功能的编程语言,可以实现各种出色的效果。HTML 是用于表示某种类型的实用计算抽象(这里指的是可由 Web 浏览器呈现的内容)的框架。Javascript 在交互式网页中的作用是 组装浏览器看到的 HTML 对象 ,然后在需要时通过将其更新为新的 HTML 来与其交互。 作者:AI前线 链接: https://juejin.im/post/5b345a49f265da599c561b25 下面再来看看TensorFlow的架构图(Python 只是 对TensorFlow进行了封装) TensorFlow的引擎也是分了很多模块的(这里我们简单了解一下): 最后 总结一下这篇文章: 安装TensorFlow必要的环境 跟着官方文档, 体验 一下TensorFlow(机器学习)的“HelloWorld” 根据给出的例子, 猜出 使用TensorFlow(机器学习)时需要做哪些步骤 TensorFlow就是一个机器学习(深度学习)的框架,提供了很多 实用 的API方便我们去玩机器学习(深度学习)。 相信看到这里,很多人都跟我之前一样, 一脸懵逼 。对官方给出的例子中的API不熟悉,去查阅相关资料的时候又发现有好多机器学习相关的 术语 ,对这些术语又不了解。然后就循环懵逼.. while(true){ // 循环懵逼.. } 其实回想 刚学Java的时候 ,也是这么一个过程,当你从屏幕看到“HelloWorld”了以后, 大概率 来说也是不了解相关的API具体做了什么事(甚至连API都不知道是啥意思)。但花一段时间去学习了以后,就会发现,原来HelloWorld是真的简单! public static void main(String[] args) { System.out.println("Hello World"); } 后续我会继续更新我学习TensorFlow时的一些 笔记 ,有兴趣的小伙伴不妨跟着我一起学习~ 另外,从文章的开头我也已经说了,我对Python、机器学习都是没有相关基础的,如果有写错的地方不妨在评论区留言指正。 下一篇预告:TensorFlow是什么意思?Tensor?Flow?介绍TensorFlow一些最基础的知识以及常见的机器学习术语。 乐于输出 干货 的Java技术公众号:Java3y。公众号内有200多篇 原创 技术文章、海量视频资源、精美脑图,不妨来 关注 一下! 觉得我的文章写得不错,不妨点一下 赞 ! 「深度学习福利」大神带你进阶工程师,立即查看>>> 《深度学习引擎-PyTorch资源集锦》包含大量与 PyTorch ( https://pytorch.org/ )相关的资源链接,带你快速玩转基于神经网络的深度学习,进入人工智能的神秘领地。链接包括:入门教程,应用实例,图像、视觉、CNN相关实现,对抗生成网络、生成模型、GAN实现,机器翻译、问答系统、NLP实现,先进视觉推理系统,深度强化学习实现,通用神经网络高级应用等等。 入门系列教程 PyTorch Tutorials https://github.com/MorvanZhou/PyTorch-Tutorial.git 著名的“莫烦”PyTorch系列教程的源码。 Deep Learning with PyTorch: a 60-minute blitz http://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html PyTorch官网推荐的由网友提供的60分钟教程,本系列教程的重点在于介绍PyTorch的基本原理,包括自动求导,神经网络,以及误差优化API。 Simple examples to introduce PyTorch https://github.com/jcjohnson/pytorch-examples.git 由网友提供的PyTorch教程,通过一些实例的方式,讲解PyTorch的基本原理。内容涉及Numpy、自动求导、参数优化、权重共享等。 PyTorch支持Kubernetes集群, https://my.oschina.net/u/2306127/blog/1817835 Kubeflow 使用指南, https://my.oschina.net/u/2306127/blog/1808582 pytorch-operator 项目源码, https://github.com/kubeflow/pytorch-operator pytorch-operator on Kubernetes, https://my.oschina.net/u/2306127/blog/1811457 入门实例 Ten minutes pyTorch Tutorial https://github.com/SherlockLiao/pytorch-beginner.git 知乎上“十分钟学习PyTorch“系列教程的源码。 Official PyTorch Examples https://github.com/pytorch/examples 官方提供的实例源码,包括以下内容: MNIST Convnets Word level Language Modeling using LSTM RNNs Training Imagenet Classifiers with Residual Networks Generative Adversarial Networks (DCGAN) Variational Auto-Encoders Superresolution using an efficient sub-pixel convolutional neural network Hogwild training of shared ConvNets across multiple processes on MNIST Training a CartPole to balance in OpenAI Gym with actor-critic Natural Language Inference (SNLI) with GloVe vectors, LSTMs, and torchtext Time sequence prediction - create an LSTM to learn Sine waves PyTorch Tutorial for Deep Learning Researchers https://github.com/yunjey/pytorch-tutorial.git 据说是提供给深度学习科研者们的PyTorch教程←_←。教程中的每个实例的代码都控制在30行左右,简单易懂,内容如下: PyTorch Basics Linear Regression Logistic Regression Feedforward Neural Network Convolutional Neural Network Deep Residual Network Recurrent Neural Network Bidirectional Recurrent Neural Network Language Model (RNN-LM) Generative Adversarial Network Image Captioning (CNN-RNN) Deep Convolutional GAN (DCGAN) Variational Auto-Encoder Neural Style Transfer TensorBoard in PyTorch PyTorch-playground https://github.com/aaron-xichen/pytorch-playground.git PyTorch初学者的Playground,在这里针对一下常用的数据集,已经写好了一些模型,所以大家可以直接拿过来玩玩看,目前支持以下数据集的模型。 mnist, svhn cifar10, cifar100 stl10 alexnet vgg16, vgg16_bn, vgg19, vgg19_bn resnet18, resnet34, resnet50, resnet101, resnet152 squeezenet_v0, squeezenet_v1 inception_v3 图像、视觉、CNN相关实现 PyTorch-FCN https://github.com/wkentaro/pytorch-fcn.git FCN(Fully Convolutional Networks implemented) 的PyTorch实现。 Attention Transfer https://github.com/szagoruyko/attention-transfer.git 论文 “Paying More Attention to Attention: Improving the Performance of Convolutional Neural Networks via Attention Transfer” 的PyTorch实现。 Wide ResNet model in PyTorch https://github.com/szagoruyko/functional-zoo.git 一个PyTorch实现的 ImageNet Classification 。 CRNN for image-based sequence recognition https://github.com/bgshih/crnn.git 这个是 Convolutional Recurrent Neural Network (CRNN) 的 PyTorch 实现。CRNN 由一些CNN,RNN和CTC组成,常用于基于图像的序列识别任务,例如场景文本识别和OCR。 Scaling the Scattering Transform: Deep Hybrid Networks https://github.com/edouardoyallon/pyscatwave.git 使用了“scattering network”的CNN实现,特别的构架提升了网络的效果。 Conditional Similarity Networks (CSNs) https://github.com/andreasveit/conditional-similarity-networks.git 《Conditional Similarity Networks》的PyTorch实现。 Multi-style Generative Network for Real-time Transfer https://github.com/zhanghang1989/PyTorch-Style-Transfer.git MSG-Net 以及 Neural Style 的 PyTorch 实现。 Big batch training https://github.com/eladhoffer/bigBatch.git 《Train longer, generalize better: closing the generalization gap in large batch training of neural networks》的 PyTorch 实现。 CortexNet https://github.com/e-lab/pytorch-CortexNet.git 一个使用视频训练的鲁棒预测深度神经网络。 Neural Message Passing for Quantum Chemistry https://github.com/priba/nmp_qc.git 论文《Neural Message Passing for Quantum Chemistry》的PyTorch实现,好像是讲计算机视觉下的神经信息传递。 对抗生成网络、生成模型、GAN相关实现 Generative Adversarial Networks (GANs) in PyTorch https://github.com/devnag/pytorch-generative-adversarial-networks.git 一个非常简单的由PyTorch实现的对抗生成网络 DCGAN & WGAN with Pytorch https://github.com/chenyuntc/pytorch-GAN.git 由中国网友实现的DCGAN和WGAN,代码很简洁。 Official Code for WGAN https://github.com/martinarjovsky/WassersteinGAN.git WGAN的官方PyTorch实现。 DiscoGAN in PyTorch https://github.com/carpedm20/DiscoGAN-pytorch.git 《Learning to Discover Cross-Domain Relations with Generative Adversarial Networks》的 PyTorch 实现。 Adversarial Generator-Encoder Network https://github.com/DmitryUlyanov/AGE.git 《Adversarial Generator-Encoder Networks》的 PyTorch 实现。 CycleGAN and pix2pix in PyTorch https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix.git 图到图的翻译,著名的 CycleGAN 以及 pix2pix 的PyTorch 实现。 Weight Normalized GAN https://github.com/stormraiser/GAN-weight-norm.git 《On the Effects of Batch and Weight Normalization in Generative Adversarial Networks》的 PyTorch 实现。 机器翻译、问答系统、NLP相关实现 DeepLearningForNLPInPytorch https://github.com/rguthrie3/DeepLearningForNLPInPytorch.git 一套以 NLP 为主题的 PyTorch 基础教程。本教程使用Ipython Notebook编写,看起来很直观,方便学习。 Practial Pytorch with Topic RNN & NLP https://github.com/spro/practical-pytorch 以 RNN for NLP 为出发点的 PyTorch 基础教程,分为“RNNs for NLP”和“RNNs for timeseries data”两个部分。 PyOpenNMT: Open-Source Neural Machine Translation https://github.com/OpenNMT/OpenNMT-py.git 一套由PyTorch实现的机器翻译系统。(包含,Attention Model) Deal or No Deal? End-to-End Learning for Negotiation Dialogues https://github.com/facebookresearch/end-to-end-negotiator.git Facebook AI Research 论文《Deal or No Deal? End-to-End Learning for Negotiation Dialogues》的 PyTorch 实现。 Attention is all you need: A Pytorch Implementation https://github.com/jadore801120/attention-is-all-you-need-pytorch.git Google Research 著名论文《Attention is all you need》的PyTorch实现。Attention Model(AM)。 Improved Visual Semantic Embeddings https://github.com/fartashf/vsepp.git 一种从图像中检索文字的方法,来自论文:《VSE++: Improved Visual-Semantic Embeddings》。 Reading Wikipedia to Answer Open-Domain Questions https://github.com/facebookresearch/DrQA.git 一个开放领域问答系统DrQA的PyTorch实现。 Structured-Self-Attentive-Sentence-Embedding https://github.com/ExplorerFreda/Structured-Self-Attentive-Sentence-Embedding.git IBM 与 MILA 发表的《A Structured Self-Attentive Sentence Embedding》的开源实现。 先进视觉推理系统 Visual Question Answering in Pytorch https://github.com/Cadene/vqa.pytorch.git 一个PyTorch实现的优秀视觉推理问答系统,是基于论文《MUTAN: Multimodal Tucker Fusion for Visual Question Answering》实现的。项目中有详细的配置使用方法说明。 Clevr-IEP https://github.com/facebookresearch/clevr-iep.git Facebook Research 论文《Inferring and Executing Programs for Visual Reasoning》的PyTorch实现,讲的是一个可以基于图片进行关系推理问答的网络。 深度强化学习相关实现 Deep Reinforcement Learning withpytorch & visdom https://github.com/onlytailei/pytorch-rl.git 多种使用PyTorch实现强化学习的方法。 Value Iteration Networks in PyTorch https://github.com/onlytailei/Value-Iteration-Networks-PyTorch.git Value Iteration Networks (VIN) 的PyTorch实现。 A3C in PyTorch https://github.com/onlytailei/A3C-PyTorch.git Adavantage async Actor-Critic (A3C) 的PyTorch实现。 通用神经网络高级应用 PyTorch-meta-optimizer https://github.com/ikostrikov/pytorch-meta-optimizer.git 论文《Learning to learn by gradient descent by gradient descent》的PyTorch实现。 OptNet: Differentiable Optimization as a Layer in Neural Networks https://github.com/locuslab/optnet.git 论文《Differentiable Optimization as a Layer in Neural Networks》的PyTorch实现。 Task-based End-to-end Model Learning https://github.com/locuslab/e2e-model-learning.git 论文《Task-based End-to-end Model Learning》的PyTorch实现。 DiracNets https://github.com/szagoruyko/diracnets.git 不使用“Skip-Connections”而搭建特别深的神经网络的方法。 ODIN: Out-of-Distribution Detector for Neural Networks https://github.com/ShiyuLiang/odin-pytorch.git 这是一个能够检测“分布不足”(Out-of-Distribution)样本的方法的PyTorch实现。当“true positive rate”为95%时,该方法将DenseNet(适用于CIFAR-10)的“false positive rate”从34.7%降至4.3%。 Accelerate Neural Net Training by Progressively Freezing Layers https://github.com/ajbrock/FreezeOut.git 一种使用“progressively freezing layers”来加速神经网络训练的方法。 Efficient_densenet_pytorch https://github.com/gpleiss/efficient_densenet_pytorch.git DenseNets的PyTorch实现,优化以节省GPU内存。 「深度学习福利」大神带你进阶工程师,立即查看>>> 项目简介: 最近在做一个有趣的项目,需要对某网站的验证码进行识别。 某网站验证码如图: ,像素大小:30x106x3 通过人工标记的验证码数量约为1000条: 现在需要通过机器学习方法来进行识别新验证码,设计的方案有如下四种: KNN + 原样本图;需要对图像去噪、二值化、切割等处理。对数据量要求没CNN高。 CNN + 原样本图;缺点:样本少,优点:数据质量高。 CNN + 构造类似验证码图;缺点:构造验证码是否和和原验证码类似,需要较高技术;优点:样本多。 CNN + 单字符样本图;优点:输入图像小,且输出类别少。 其他:如用pytesseract+去噪+二值化等,简单尝试了一下准确率很低,pass掉了 方案一:KNN + 原样本图 步骤: 去噪: 原图: class NoiseDel(): #去除干扰噪声 def noise_del(self,img): height = img.shape[0] width = img.shape[1] channels = img.shape[2] # 清除四周噪点 for row in [0,height-1]: for column in range(0, width): if img[row, column, 0] == 0 and img[row, column, 1] == 0: img[row, column, 0] = 255 img[row, column, 1] = 255 for column in [0,width-1]: for row in range(0, height): if img[row, column, 0] == 0 and img[row, column, 1] == 0: img[row, column, 0] = 255 img[row, column, 1] = 255 # 清除中间区域噪点 for row in range(1,height-1): for column in range(1,width-1): if img[row, column, 0] == 0 and img[row, column, 1] == 0: a = img[row - 1, column] # 上 b = img[row + 1, column] # 下 c = img[row, column - 1] # 左 d = img[row, column + 1] # 右 ps = [p for p in [a, b, c, d] if 1 < p[1] < 255] # 如果上下or左右为白色,设置白色 if (a[1]== 255 and b[1]== 255) or (c[1]== 255 and d[1]== 255): img[row, column, 0] = 255 img[row, column, 1] = 255 # 设置灰色 elif len(ps)>1: kk = np.array(ps).mean(axis=0) img[row, column, 0] = kk[0] img[row, column, 1] = kk[1] img[row, column, 2] = kk[2] else: img[row, column, 0] = 255 img[row, column, 1] = 255 return img # 灰度化 def convert2gray(self,img): if len(img.shape) > 2: gray = np.mean(img, -1) # 上面的转法较快,正规转法如下 # r, g, b = img[:,:,0], img[:,:,1], img[:,:,2] # gray = 0.2989 * r + 0.5870 * g + 0.1140 * b return gray else: return img # 二值化 def binarizing(self,img,threshold, cov=False): w, h = img.shape if cov: for y in range(h): for x in range(w): if img[x, y] > threshold: img[x, y] = 0 else: img[x, y] = 255 else: for y in range(h): for x in range(w): if img[x, y] < threshold: img[x, y] = 0 else: img[x, y] = 255 return img 去噪后: 切分最小图 def cut_box(img,resize=(64,18)): # 灰度,二值化 image = nd.convert2gray(img) image = nd.binarizing(image,190, True) image = Image.fromarray(image) img0 = Image.fromarray(img) box = image.getbbox() box1 = (box[0]-2,box[1]-2,box[2]+2,box[3]+2) image = img0.crop(box1) image = image.resize(resize) return np.array(image) 切分后: 分割字符串: def seg_img(img): h,w,c = img.shape d = int(w/4) img_list = [] for i in range(4): img_list.append(img[:,i*d:(i+1)*d]) return img_list 分割后: , , , 经过对1000张标记好的图片进行处理,得到各个字母数字对应的单字符图片数据集: KNN训练及预测: 对图像进行灰度处理 import os from PIL import Image import numpy as np from cut_prc import cut_box,seg_img from noise_prc import NoiseDel from sklearn import neighbors, svm,tree,linear_model from sklearn.model_selection import train_test_split nd = NoiseDel() def predict_img(img, clf): text = '' image = nd.noise_del(img) image = cut_box(image) image_list = seg_img(image) for im in image_list: im = nd.convert2gray(im) im = im.reshape(-1) c = clf.predict([im,])[0] text += c return text if __name__=="__main__": # 获取训练数据 path = 'data/png_cut' classes = os.listdir(path) x = [] y = [] for c in classes: c_f = os.path.join(path, c) if os.path.isdir(c_f): files = os.listdir(c_f) for file in files: img = Image.open(os.path.join(c_f, file)) img = np.array(img) img = nd.convert2gray(img) img = img.reshape(-1) x.append(img) y.append(c.replace('_','')) x = np.array(x) y = np.array(y) # 拆分训练数据与测试数据 x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.02) # 训练KNN分类器 clf = neighbors.KNeighborsClassifier(n_neighbors=5) clf.fit(x_train, y_train) # 预测测试集 test_pre = clf.predict(x_test) print("KNN accuracy score:", (test_pre == y_test).mean()) # 预测新样本集 newpath = 'data/png_new' newfiles = os.listdir(newpath) for file in newfiles: image = Image.open(os.path.join(newpath,file)) image = np.array(image) text = predict_img(image, clf) print(text) # # 训练svm分类器 # clf = svm.LinearSVC() ### # clf.fit(x_train, y_train) # # test_pre = clf.predict(x_test) # print("SVM accuracy score:", (test_pre == y_test).mean()) # # # 训练dt分类器 # clf = tree.DecisionTreeClassifier() # clf.fit(x_train, y_train) # # test_pre = clf.predict(x_test) # print("DT accuracy score:", (test_pre == y_test).mean()) # # # 训练lr分类器 # clf = linear_model.LogisticRegression() ### t # clf.fit(x_train, y_train) # # test_pre = clf.predict(x_test) # print("LR accuracy score:", (test_pre == y_test).mean()) 运行结果:(单个字符预测精度),KNN最高,达到80%,而SVM,DT,LR均较低 KNN accuracy score: 0.8170731707317073 SVM accuracy score: 0.6341463414634146 DT accuracy score: 0.4878048780487805 LR accuracy score: 0.5975609756097561 KNN 预测图片: mHFM crdN wa5Y swFn ApB9 eBrN rJpH fd9e kTVt t7ng 方案二:CNN+原样本图 步骤: 处理样本数据1020张图,灰度化 ,像素大小30*106,标签为小写字符(标记人员太懒了); 拆分数据:train:80%, val:20% 网络模型:输入数据维度30*106,采用三层CNN,每一层输出特征维数分别:16,128,16,FC层输出 512维,最后全连接输出4x63,每行代表预测字符的概率。 结果:验证集字符准确率最高到达了50% 方案三: CNN+ 构造类似验证码图 第三方库生成的验证码如下所示: from captcha.image import ImageCaptcha # pip install captcha 下载相应的字体(比较难找),然后修改第三方库中image.py文件,修改了第三方库后生成的验证码: 效果和我们需要的验证码比较相似了,但还是有区别。 fonts = ["font/TruenoBdOlIt.otf", "font/Euro Bold.ttf", "STCAIYUN.TTF"] image = ImageCaptcha(width=106, height=30,fonts=[fonts[0]],font_sizes=(18,18,18)) captcha = image.generate(captcha_text) image.py 略.. 采用自动生成的验证码,用于CNN训练模型,训练和验证精度都达到了98%,但测试原图1000样本的字符精度最高只有40%,由此可见,生成的验证码还是与目标验证码相差较大。 step: 18580/20000... loss: 0.0105... step: 18600/20000... loss: 0.0121... step: 18600/20000... --------- val_acc: 0.9675 best: 0.9775 --------- test_acc2: 0.4032 step: 18620/20000... loss: 0.0131... step: 18640/20000... loss: 0.0139... step: 18660/20000... loss: 0.0135... step: 18680/20000... loss: 0.0156... step: 18700/20000... loss: 0.0109... step: 18700/20000... --------- val_acc: 0.9625 best: 0.9775 --------- test_acc2: 0.3995 方案四: CNN+ 字符样本集 由于只有1000样本,直接经过CNN端到端输出字符序列,很难到达精度要求,为此方案三采用自动创建样本集的方法,但样本质量和真实样本之间存在差异,导致预测不准。为此,将原1000样本进行分割处理为单字符集,样本数量约4000左右,且输入维度减小很多,同时输出类别也减小很多。分析后改方案有一定可行性。 样本集处理与之前KNN一样: 经过对1000张标记好的图片进行处理,得到各个字母数字对应的单字符图片数据集: import os import numpy as np import tensorflow as tf from PIL import Image from noise_prc import NoiseDel from cut_prc import cut_box,seg_img from sklearn.model_selection import train_test_split from sklearn.preprocessing import LabelBinarizer nd = NoiseDel() class Config(): file_name = 'char_02' # 存放模型文件夹 w_alpha = 0.01 #cnn权重系数 b_alpha = 0.1 #cnn偏执系数 image_h = 18 image_w = 16 cnn_f = [16,32,32,512] # [cov1输出特征维度,cov2输出特征维度,cov3输出特征维度,全连接层输出维度] max_captcha = 1 #验证码最大长度 char_set_len = 50 #字符集长度 lr = 0.001 batch_size = 32 # 每批训练大小 max_steps = 200000 # 总迭代batch数 log_every_n = 20 # 每多少轮输出一次结果 save_every_n = 100 # 每多少轮校验模型并保存 class Model(): def __init__(self, config): self.config = config self.input() self.cnn() # 初始化session self.saver = tf.train.Saver() self.session = tf.Session() self.session.run(tf.global_variables_initializer()) def input(self): self.X = tf.placeholder(tf.float32, [None, self.config.image_h * self.config.image_w]) self.Y = tf.placeholder(tf.float32, [None, self.config.max_captcha * self.config.char_set_len]) self.keep_prob = tf.placeholder(tf.float32) # dropout # 两个全局变量 self.global_step = tf.Variable(0, trainable=False, name="global_step") self.global_loss = tf.Variable(0, dtype=tf.float32, trainable=False, name="global_loss") def cnn(self): x = tf.reshape(self.X, shape=[-1, self.config.image_h , self.config.image_w, 1]) # 3 conv layer w_c1 = tf.Variable(self.config.w_alpha * tf.random_normal([3, 3, 1, self.config.cnn_f[0]])) b_c1 = tf.Variable(self.config.b_alpha * tf.random_normal([self.config.cnn_f[0]])) conv1 = tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(x, w_c1, strides=[1, 1, 1, 1], padding='SAME'), b_c1)) conv1 = tf.nn.max_pool(conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME') conv1 = tf.nn.dropout(conv1, self.keep_prob) w_c2 = tf.Variable(self.config.w_alpha * tf.random_normal([3, 3, self.config.cnn_f[0], self.config.cnn_f[1]])) b_c2 = tf.Variable(self.config.b_alpha * tf.random_normal([self.config.cnn_f[1]])) conv2 = tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(conv1, w_c2, strides=[1, 1, 1, 1], padding='SAME'), b_c2)) conv2 = tf.nn.max_pool(conv2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME') conv2 = tf.nn.dropout(conv2, self.keep_prob) w_c3 = tf.Variable(self.config.w_alpha * tf.random_normal([3, 3, self.config.cnn_f[1], self.config.cnn_f[2]])) b_c3 = tf.Variable(self.config.b_alpha * tf.random_normal([self.config.cnn_f[2]])) conv3 = tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(conv2, w_c3, strides=[1, 1, 1, 1], padding='SAME'), b_c3)) conv3 = tf.nn.max_pool(conv3, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME') conv3 = tf.nn.dropout(conv3, self.keep_prob) # Fully connected layer h =tf.cast( conv3.shape[1],tf.int32) w = tf.cast( conv3.shape[2],tf.int32) f = tf.cast( conv3.shape[3],tf.int32) w_d = tf.Variable(self.config.w_alpha * tf.random_normal([h* w * f, self.config.cnn_f[3]])) b_d = tf.Variable(self.config.b_alpha * tf.random_normal([self.config.cnn_f[3]])) dense = tf.reshape(conv3, [-1, w_d.get_shape().as_list()[0]]) dense = tf.nn.relu(tf.add(tf.matmul(dense, w_d), b_d)) dense = tf.nn.dropout(dense, self.keep_prob) w_out = tf.Variable(self.config.w_alpha * tf.random_normal([self.config.cnn_f[3], self.config.max_captcha * self.config.char_set_len])) b_out = tf.Variable(self.config.b_alpha * tf.random_normal([self.config.max_captcha * self.config.char_set_len])) out = tf.add(tf.matmul(dense, w_out), b_out) # out = tf.nn.softmax(out) # loss # loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(output, Y)) self.loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=out, labels=self.Y)) # optimizer 为了加快训练 learning_rate应该开始大,然后慢慢衰 self.optimizer = tf.train.AdamOptimizer(learning_rate=self.config.lr).minimize(self.loss,global_step=self.global_step) predict = tf.reshape(out, [-1, self.config.max_captcha, self.config.char_set_len]) self.max_idx_p = tf.argmax(predict, 2) max_idx_l = tf.argmax(tf.reshape(self.Y, [-1, self.config.max_captcha, self.config.char_set_len]), 2) correct_pred = tf.equal(self.max_idx_p, max_idx_l) self.accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32)) def load(self, checkpoint): self.saver.restore(self.session, checkpoint) print('Restored from: {}'.format(checkpoint)) def train(self, get_next_batch, model_path, x_train, y_train, x_test, y_test): with self.session as sess: while True: batch_x, batch_y = get_next_batch(x_train, y_train,self.config.batch_size) _, loss_ = sess.run([self.optimizer, self.loss], feed_dict={self.X: batch_x, self.Y: batch_y, self.keep_prob: 0.75}) if self.global_step.eval() % self.config.log_every_n == 0: print('step: {}/{}... '.format(self.global_step.eval(), self.config.max_steps), 'loss: {:.4f}... '.format(loss_)) # 每100 step计算一次准确率 if self.global_step.eval() % self.config.save_every_n == 0: # batch_x_test, batch_y_test = get_next_batch(100) acc = sess.run(self.accuracy, feed_dict={self.X: x_test, self.Y: y_test, self.keep_prob: 1.}) print('step: {}/{}... '.format(self.global_step.eval(), self.config.max_steps), '--------- acc: {:.4f} '.format(acc), ' best: {:.4f} '.format(self.global_loss.eval())) if acc > self.global_loss.eval(): print('save best model...') update = tf.assign(self.global_loss, acc) # 更新最优值 sess.run(update) self.saver.save(sess, os.path.join(model_path, 'model'), global_step=self.global_step) if self.global_step.eval() >= self.config.max_steps: #self.saver.save(sess, os.path.join(model_path, 'model_last'), global_step=self.global_step) break def test(self, batch_x_test): sess = self.session max_idx_p = sess.run(self.max_idx_p, feed_dict={self.X: batch_x_test, self.keep_prob: 1.}) return max_idx_p def get_next_batch( train_x, train_y, batch_size=32): n = train_x.shape[0] chi_list = np.random.choice(n, batch_size) return train_x[chi_list],train_y[chi_list] def img_cut_to_arry(img): imgs = [] image = nd.noise_del(img) image = cut_box(image) image_list = seg_img(image) for im in image_list: im = nd.convert2gray(im) im = im.reshape(-1) imgs.append(im) return imgs if __name__=="__main__": # nd = NoiseDel() # 获取训练数据 path = 'data/png_cut' classes = os.listdir(path) x = [] y = [] for c in classes: c_f = os.path.join(path, c) if os.path.isdir(c_f): files = os.listdir(c_f) for file in files: img = Image.open(os.path.join(c_f, file)) img = np.array(img) img = nd.convert2gray(img) img = img.reshape(-1) x.append(img) y.append(c.replace('_','')) lb = LabelBinarizer() ly = lb.fit_transform(y) # one-hot x = np.array(x) y = np.array(ly) # 拆分训练数据与测试数据 x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.02) # 创建模型目录 model_path = os.path.join('c_models', Config.file_name) if os.path.exists(model_path) is False: os.makedirs(model_path) # 加载上一次保存的模型 model = Model(Config) checkpoint_path = tf.train.latest_checkpoint(model_path) if checkpoint_path: model.load(checkpoint_path) # train and val print('start to training...') model.train(get_next_batch, model_path, x_train,y_train, x_test, y_test) # test # 预测新样本集 newpath = 'data/png_new' newfiles = os.listdir(newpath) for file in newfiles: pre_text='' image = Image.open(os.path.join(newpath,file)) image = np.array(image) imgs= img_cut_to_arry(image) for img in imgs: p = model.test([img,]) p_arr = np.zeros([1,50]) p_arr[0,p] =1 c = lb.inverse_transform(p_arr) pre_text += c[0] print(pre_text) 运行结果:字符预测精度95%以上 step: 2500/200000... loss: 0.0803... step: 2500/200000... --------- acc: 0.0854 best: 0.1341 step: 2520/200000... loss: 0.0818... step: 2540/200000... loss: 0.0844... step: 2560/200000... loss: 0.0827... step: 2580/200000... loss: 0.0794... step: 2600/200000... loss: 0.0823... step: 2600/200000... --------- acc: 0.1951 best: 0.1341 save best model... step: 2620/200000... loss: 0.0775... step: 2640/200000... loss: 0.0754... step: 2660/200000... loss: 0.0823... step: 2680/200000... loss: 0.0678... step: 2700/200000... loss: 0.0763... step: 2700/200000... --------- acc: 0.3049 best: 0.1951 . . . . step: 41400/200000... --------- acc: 0.8659 best: 0.9512 step: 41450/200000... loss: 0.0091... step: 41500/200000... loss: 0.0134... step: 41550/200000... loss: 0.0151... step: 41600/200000... loss: 0.0256... step: 41600/200000... --------- acc: 0.9390 best: 0.9512 预测图片: mHPM srdN wa5Y eWpn AgB9 eHr8 rJpH fd9e bTYt tTwg 最后,附上源码地址: github 「深度学习福利」大神带你进阶工程师,立即查看>>> 深度学习与处理用于数据驱动的地球系统科学 由Markus Reichstein 在 2019 发表于 《Nature》。 这是一篇综述性文章,介绍了机器学习如何在地学中的应用与挑战。地球系统科学进入了大数据时代。地球系统数据就是典型的大数据,具备大数据四大特征:volume, velocity, variety and veracity(体积,速度,多样性和准确性),例如各种遥感、定点观测、模式数据。如今面临挑战就是如何从这些大数据中提取并解读信息,因为信息收集速度远大于我们所能消化的速度。数据的增多并未带对系统预测能力的提高,我们需要对数据进行理解。在这种背景下,机器学习就是我们很好的机遇。 文章从以几方面具体展开论述: (1)地学中最先进的机器学习 。诸如神经网络、随机森林方法很早就应用于地学中的分类、变化检测、土壤制图问题。但这些应用是针对空间,在时间上是相对静态的,但地球是不断变化的。机器学习回归方法在时间动态上具有优势,比如具有隐含层的人工神经网络,可预测碳通量在时间与空间上的变化。但这些应用也存在一些问题需要注意,比如外推能力,抽样或数据偏见,忽视混杂因素,统计关联与因果关系等。经典的机器学习方法需要一些先验知识确定一些时空相关feature,而不能自动探索数据的时空特征。一些时空动态特征比如“记忆效应”可以作为feature手动加入到传统机器学习中,但最新的深度学习已经没有这些限制。 (2)深度学习在地球系统科学中的机遇。 深度学习已在其他领域得到了众多应用,但在地学中的应用还处于初级阶段。已有一些研究显示深度学习可以很好的提取时空特征,比如极端天气,而不需要很多人类干预。这也可用于城市变化的遥感自动提取。深度学习方法通常被划分为空间学习(例如,用于对象分类的卷积神经网络)和序列学习(例如,语音识别),但两者逐渐融合,可应用视频与动作识别问题。这些问题类似于地学中随时间变化的多维度结构,例如有序降水对流与植被状态。虽然有很大应用前景,但应用于时空变化的大气海洋传输或植被动态还有待发展。 (3)深度学习在地球系统科学中的挑战。 虽然传统深度学习的应用对象与地学现象有很大相似性,但也存在重要区别。比如高光谱、多波段就比基于三原色RGB的计算机图像识别复杂很多,此外还有带噪音、有缺测的卫星数据。另外,波段、时间与空间维度的集合也会带来计算量的挑战。计算机图片中识别可大量“狗”,“猫”现成训练样本,而地学中没有类似被标记的大量训练样本,如干旱。对外,作者总结出五大挑战,分别来自可解释性、物理一致性、数据的复杂与确定性、缺少标记样本、以及计算需求。若这些挑战能解决,那么深度学习将对地学带来巨大改变。近期最有前景的应用是”临近预报“(nowcasting),未来是长期预测。作者认为深度学习将很快成为地学中分类与时空预测问题的主要方法。 (4) 与物理建模集成。 物理建模(理论驱动)与机器学习建模(数据驱动)过去往往被认为是两个领域,具有不同范式。但其实两种方法可以相互补充的,前者外推能力强,后者更灵活可发现新规律。作者提出二种方法可结合的几个潜在点:改善参数化、用机器学习“替代”物理模型中子模块、模型与观测的不匹配分析,约束子模型、代替模型或仿真。 (5)推动科学发展。 机器学习方法无疑给分类和预测问题带来大幅提高。机器学习的数据驱动方法还可从数据中挖掘出过去不知道的新信息,从而推动新机制新认识的产生。 (6)结语。 地球科学大数据时代机器学习很有用,但也存在应用挑战,作者对此提出四点建议:识别数据的特殊性、推论的合理性和可解释性、不确定性估计、针对复杂物理模式进行验证。未来过程模型与机器学习将进一步结合。数据驱动的机器学习不会替代物理模型,但是会起到补充和丰富的作用,最终实现混合建模。 第一作者Markus Reichstein是大牛,单位是1 Department of Biogeochemical Integration, Max Planck Institute for Biogeochemistry, Jena, Germany. 2 Michael-Stifel-Center Jena for Data-driven and Simulation Science, Jena, Germany. 来源, https://mp.weixin.qq.com/s/uL4C3birbbFQ-UhntuTgRg 英文, https://www.nature.com/articles/s41586-019-0912-1 「深度学习福利」大神带你进阶工程师,立即查看>>> Hanlp在离线环境下的安装我是没有尝试过的,分享SunJW_2017的这篇文章就是关于如何在离线环境下安装hanlp的。我们可以一起来学习一下! HanLP是一款优秀的中文自然语言处理工具,可以实现包括分词、词性标注、实体识别在内容诸多功能,详情见其项目主页。另外,该工具包还提供了python3的接口,在线安装很方便,但有时需要部署在内网机等没有网络环境的机器上,比较繁琐,整理步骤如下: 1、安装jpype1,这是pyhanlp的依赖包,可以下载编译好的.whl文件,地址在这,选择对应自己python版本和操作系统版本的后,用pip install *.whl进行安装即可。 2、安装java环境,由于HanLP是用java开发的,因此调用python的接口时机器上应该配置了java环境,可以在网上搜索安装。 3、在pyhanlp的项目主页下载完整的.zip压缩包,解压后进入文件夹执行python setup.py install。 4、下载data-for-1.7.0.zip和hanlp-1.7.0.-release.zip,解压后分别获得data文件夹和hanlp.properties、hanlp-1.7.0.jar文件。 5、进入python包的安装目录,以Anaconda为例,进入该安装目录下的./lib/site-packages/pyhanlp-0.1.44-py3.6.egg/pyhanlp文件夹,将4中得到的文件复制到本目录,保证目录的内部结构如下: hanlp |—static | |—data | | |—dictionary | | |—model | | |—READ.html | | |—version.txt | |—init.py | |—hanlp.properties | |—hanlp.properties.in | |—hanlp-1.7.0.jar | |—index.html |—init.py |—main.py |—server.py |—util.py 6、编辑hanlp.properties中的内容,更改其中root=行的内容,使其指向data文件夹的上一层,如root=D:/Anaconda/Lib/site-packages/pyhanlp-0.1.44-py3.6.egg/pyhanlp/static。 7、打开命令行,输入hanlp -v,检查是否安装成功。正常应该返回jar包、data文件夹和hanlp.properties的位置。 --------------------- 「深度学习福利」大神带你进阶工程师,立即查看>>> 根据 Gartner 对全球 CIO 的调查结果显示,人工智能将成为 2019 年组织革命的颠覆性力量。对于人工智能来说,算力即正义,成本即能力,利用 Docker 和 Kubernetes 代表云原生技术为 AI 提供了一种新的工作模式,将 GPU 机器放到统一的资源池进行调度和管理,这避免了GPU 资源利用率低下和人工管理的成本。因此,全球主要的容器集群服务厂商 Kubernetes 都提供了 Nvidia GPU 容器集群调度能力,但是通常都是将一个 GPU 卡分配给一个容器。这虽然可以实现比较好的隔离性,确保使用 GPU 的应用不会被其他应用影响;对于深度学习模型训练的场景也非常适合,但是,针对模型开发和模型预测的场景还是会显得比较浪费。基于此,大家有了共享 GPU 的集群调度需求。 Kubernetes 共享 GPU 集群调度 共享 GPU 的集群调度就是能够让更多的模型开发和预测服务共享同一个 GPU 卡,进而提高集群中 Nvidia GPU 的利用率。而这就需要提供 GPU 资源的划分,而这里 GPU 资源划分的维度指的就是 GPU 显存和 Cuda Kernel 线程的划分。通常在集群级别谈支持共享 GPU 是以下两件事情: 1.调度 2.隔离,我们这里主要讨论的是调度,隔离的方案目前需要用户通过应用限制(比如使用 Tensorflow 的per_process_gpu_memory_fraction 来控制),未来会提供基于 Nvidia 的 MPS 的可选项, 也会考虑 GPU 的方案。 而对于细粒度的 GPU 卡调度,目前 Kubernetes 社区并没有很好的方案,这是由于 Kubernetes 对于 GPU 这类扩展资源的定义仅仅支持整数粒度的加加减减,无法支持复杂资源的分配。比如用户希望使用 Pod A 占用半张 GPU卡,这在目前 Kubernetes 的架构设计中无法实现资源分配的记录和调用。这里挑战是多卡 GPU 共享是实际矢量资源问题,而 Extened Resource 是标量资源的描述。 针对此问题,我们设计了一个 Out Of Tree 的共享 GPU 调度方案,该方案依赖于 Kubernetes 的现有的工作机制: Extended Resource 定义 Scheduler Extender 机制 Device Plugin 机制 Kubectl 的扩展机制 这个 GPU 共享调度扩展的好处是:利用 Kubernetes 的扩展和插件机制实现,对于 API Server,Scheduler,Controller Manager 以及 Kubelet 等核心组件没有侵入性。这就方便了使用者可以在不同 Kubernetes 版本上应用这个方案,无需 rebase 代码和重新构建 Kubernetes 二进制包。 用户场景 集群管理员:“我想提高集群的 GPU 使用率;在开发过程中,多个用户共享模型开发环境。” 应用开发人员:“我希望能够同时在 Volta GPU 上运行多个推理任务。” []( https://www.atatech.org/articles/132268#2) 目标 能够让使用者通过 API 描述对于一个可共享资源的申请, 并能实现该种资源的调度 []( https://www.atatech.org/articles/132268#3) 非目标 不支持该共享资源的隔离 不支持超卖 []( https://www.atatech.org/articles/132268#4) 设计原则 明确问题简化设计,第一步只负责调度和部署,后续再实现运行时显存管控。 有很多的客户明确的诉求是首先可以支持多AI应用可以调度到同一个 GPU 上,他们可以接受从应用级别控制显存的大小,利用类似 gpu_options.per_process_gpu_memory_fraction 控制应用的显存使用量。那我们要解决的问题就先简化到以显存为调度标尺,并且把显存使用的大小以参数的方式传递给容器内部。 不做侵入式修改 本设计中不会修改 Kubernetes 核心的 Extended Resource 的设计, Scheduler 的实现,Device Plugin 的机制以及 Kubelet 的相关设计。重用 Extended Resource 描述共享资源的申请 API。这样的好处在于提供一个可以移植的方案,用户可以在原生 Kubernetes 上使用这个方案。 按显存和按卡调度的方式可以在集群内并存,但是同一个节点内是互斥的,不支持二者并存;要么是按卡数目,要么是按显存分配。 详细设计 []( https://www.atatech.org/articles/132268#6) 前提: 依旧延用 Kubernetes Extended Resource 定义,但是衡量维度最小单位从 1 个 GPU 卡变为 GPU 显存的 MiB。如果所节点使用的 GPU 为单卡 16GiB 显存,它对应的资源就是 16276MiB; 由于用户对于共享GPU的诉求在于模型开发和模型预测场景,在此场景下,用户申请的GPU资源上限不会超过一张卡,也就是申请的资源上限为单卡。 而我们的工作首先是定义了两个新的 Extended Resource: 第一个是 gpu-mem, 对应的是 GPU 显存;第二个是 gpu-count,对应的是 GPU 卡数。 通过两个标量资源描述矢量资源, 并且结合这一资源,提供支持共享 GPU 的工作机制。下面是基本的架构图: []( https://www.atatech.org/articles/132268#7) 核心功能模块: GPU Share Scheduler Extender : 利用 Kubernetes 的调度器扩展机制,负责在全局调度器 Filter 和 Bind 的时候判断节点上单个 GPU 卡是否能够提供足够的 GPU Mem,并且在 Bind 的时刻将 GPU 的分配结果通过 annotation 记录到 Pod Spec 以供后续 Filter 检查分配结果。 GPU Share Device Plugin : 利用 Device Plugin 机制,在节点上被 Kubelet 调用负责 GPU 卡的分配,依赖 scheduler Extender 分配结果执行。 []( https://www.atatech.org/articles/132268#8) 具体流程: 资源上报 GPU Share Device Plugin 利用 nvml 库查询到 GPU 卡的数量和每张 GPU 卡的显存, 通过 ListAndWatch() 将节点的 GPU 总显存(数量 显存)作为另外 Extended Resource 汇报给 Kubelet; Kubelet 进一步汇报给 Kubernetes API Server。 举例说明,如果节点含有两块 GPU 卡,并且每块卡包含 16276MiB,从用户的角度来看:该节点的 GPU 资源为 16276 2 = 32552; 同时也会将节点上的 GPU 卡数量 2 作为另外一个 Extended Resource 上报。 2. 扩展调度 GPU Share Scheduler Extender 可以在分配 gpu-mem 给 Pod 的同时将分配信息以 annotation 的形式保留在 Pod spec 中,并且在过滤时刻根据此信息判断每张卡是否包含足够可用的 gpu-mem 分配。 2.1 Kubernetes 默认调度器在进行完所有过滤(filter)行为后会通过 http 方式调用 GPU Share Scheduler Extender的filter 方法, 这是由于默认调度器计算 Extended Resource 时,只能判断资源总量是否有满足需求的空闲资源,无法具体判断单张卡上是否满足需求;所以就需要由 GPU Share Scheduler Extender 检查单张卡上是否含有可用资源。 以下图为例, 在由 3 个包含两块 GPU 卡的节点组成的 Kubernetes 集群中,当用户申请 gpu-mem=8138 时,默认调度器会扫描所有节点,发现 N1 所剩的资源为 (16276 * 2 - 16276 -12207 = 4069 )不满足资源需求,N1 节点被过滤掉。 而 N2 和 N3 节点所剩资源都为 8138MiB,从整体调度的角度看,都符合默认调度器的条件;此时默认调度器会委托 GPU Share Scheduler Extender 进行二次过滤,在二次过滤中,GPU Share Scheduler Extender 需要判断单张卡是否满足调度需求,在查看 N2 节点时发现该节点虽然有 8138MiB 可用资源,但是落到每张卡上看,GPU0 和分别 GPU1 只有 4069MiB 的可用资源,无法满足单卡 8138MiB 的诉求。而 N3 节点虽然也是总共有 8138MiB 可用资源,但是这些可用资源都属于 GPU0,满足单卡可调度的需求。由此,通过 GPU Share Scheduler Extender 的筛选就可以实现精准的条件筛选。 2.2 当调度器找到满足条件的节点,就会委托 GPU Share Scheduler Extender 的 bind 方法进行节点和 Pod 的绑定,这里 Extender 需要做的是两件事情 以 binpack 的规则找到节点中最优选择的 GPU 卡 id,此处的最优含义是对于同一个节点不同的 GPU 卡,以 binpack 的原则作为判断条件,优先选择空闲资源满足条件但同时又是所剩资源最少的 GPU 卡,并且将其作为 ALIYUN_COM_GPU_MEM_IDX 保存到 Pod 的 annotation 中;同时也保存该 Pod 申请的 GPU Memory 作为 ALIYUN_COM_GPU_MEM_POD 和 ALIYUN_COM_GPU_MEM_ASSUME_TIME 保存至 Pod 的 annotation 中,并且在此时进行 Pod 和所选节点的绑定。 注意:这时还会保存 ALIYUN_COM_GPU_MEM_ASSIGNED 的 Pod annotation,它被初始化为“false”。它表示该 Pod 在调度时刻被指定到了某块 GPU 卡,但是并没有真正在节点上创建该 Pod。 ALIYUN_COM_GPU_MEM_ASSUME_TIME 代表了 指定 时间。 如果此时发现分配节点上没有 GPU 资源符合条件,此时不进行绑定,直接不报错退出,默认调度器会在 assume 超时后重新调度。 调用 Kubernetes API 执行节点和 Pod 的绑定 以下图为例,当 GPU Share Scheduler Extender 要把 gpu-mem:8138 的 Pod 和经过筛选出来的节点 N1 绑定,首先会比较不同 GPU 的可用资源,分别为 GPU0(12207),GPU1(8138),GPU2(4069),GPU3(16276),其中 GPU2 所剩资源不满足需求,被舍弃掉;而另外三个满足条件的 GPU 中, GPU1 恰恰是符合空闲资源满足条件但同时又是所剩资源最少的 GPU 卡,因此 GPU1 被选出。
3. 节点上运行 当 Pod 和节点绑定的事件被 Kubelet 接收到后,Kubelet 就会在节点上创建真正的 Pod 实体,在这个过程中, Kubelet 会调用 GPU Share Device Plugin 的 Allocate 方法, Allocate 方法的参数是 Pod 申请的 gpu-mem。而在 Allocate 方法中,会根据 GPU Share Scheduler Extender 的调度决策运行对应的 Pod 会列出该节点中所有状态为 Pending 并且 ALIYUN_COM_GPU_MEM_ASSIGNED 为 false 的 GPU Share Pod 选择出其中 Pod Annotation 的 ALIYUN_COM_GPU_MEM_POD 的数量与 Allocate 申请数量一致的 Pod。如果有多个符合这种条件的 Pod,就会选择其中 ALIYUN_COM_GPU_MEM_ASSUME_TIME 最早的 Pod。 将该 Pod 的 annotation ALIYUN_COM_GPU_MEM_ASSIGNED 设置为 true ,并且将 Pod annotation 中的 GPU 信息转化为环境变量返回给 Kubelet 用以真正的创建 Pod。 []( https://www.atatech.org/articles/132268#9) 相关项目 目前项目已经开源到 github.com 上 gpushare-scheduler-extender gpushare-device-plugin 部署 请参照 部署文档 []( https://www.atatech.org/articles/132268#11) 测试样例 首先创建一个使用 aliyun.com/gpu-mem 的应用 apiVersion: apps/v1 kind: Deployment metadata: name: binpack-1 labels: app: binpack-1 spec: replicas: 1 selector: # define how the deployment finds the pods it manages matchLabels: app: binpack-1 template: # define the pods specifications metadata: labels: app: binpack-1 spec: containers: - name: binpack-1 image: cheyang/gpu-player:v2 resources: limits: # MiB aliyun.com/gpu-mem: 1024 使用 请参照 使用文档 构建 请参照 如何构建 视频 Demo []( https://www.atatech.org/articles/132268#15)Demo 1: 部署多个 GPU Share 的 Pod,发现他们以 binpack 的方式被放置到同一个 GPU 卡上 []( https://www.atatech.org/articles/132268#16)Demo 2: 避免错误调度申请资源超过单个 GPU 可用资源的 Pod Roadmap 在 Device Plugin 中提供 Nvidia MPS 的可选支持; 支持该方案可以在由 kubeadm 初始化的 Kubernetes 集群自动化部署; 提升 Scheduler Extener 的高可用性; 为 GPU, RDMA 和弹性网卡提供通用方案。 原文链接 「深度学习福利」大神带你进阶工程师,立即查看>>> 前言: 搜索是人们在海量信息中获取有效信息的一种常见方式。 与日常“文字搜索”不同,图像搜索是通过“以图搜图”的方式来获取人们想要的图片信息。 当前,图像搜索正在以更广泛的应用、更形象的体验迅速崛起,获得越来越多的市场关注,被公认为当前最具有市场潜力的应用之一,并且在电子商务、知识产权保护、服装纺织、旅游等领域逐步得到广泛应用。 演讲嘉宾简介: 安永,阿里巴巴数据智能产品运营专家,来自阿里云智能事业群。 以下内容根据演讲嘉宾视频分享以及PPT整理而成。 本次主要内容分为三部分: 一、市场分析: 图像搜索被公认为最具市场潜力的应用之一。 二、产品介绍: 一图胜千言,从兴趣意念直达搜索目标。 三、用户分析: 图像搜索已经在各种行业领域有了广泛的应用。 一、市场分析 如果从市场角度去看,国内的电商,无论是巨头,譬如天猫、淘宝;还是垂直电商以及跨境电商类的平台越来越多,无论平台大小,这些都是我们今天图像搜索这款产品潜在的目标用户。 用户购买产品时对内容检索的需求是非常的强烈,要求也非常高,而且需要强调的一点是:他们的使用场景不会是在某个安静的环境去购买,在上下班路上、在逛超市的时候,有你喜欢的一款衣服,结果没有合适的码数,这个时候你打开淘宝,搜索框有个拍照按钮,拍张照片一搜,直接下单,第二天心仪的宝贝就到手了,你们说方便吗~~ 这就是我们拍立淘这款工具,我们可以直接在天猫、淘宝里面检索相应图片,事实证明的话,拍立淘的购买转化率也会高15%左右。 另外就是国内的图库IP公司。我们拿到了04年到16年的一个数据,可以直接看到图片素材的交易量有了快速增长的趋势,国内也有很多这样的客户,包括视觉中国、东方IC… 在互联网上也有各种图片,譬如论坛,或者说旅游网站中那些图片也是属于我们图像搜索的范围内的,这些也都是我们的潜在客户! 二.图像搜索产品介绍 图像搜索服务 (Image Search) 是以深度学习和机器视觉技术为核心,结合不同行业应用和业务场景。 概览: 帮助用户在自建图库中实现相同或相似图片搜索的以图搜图服务。 我们产品是基于阿里巴巴电商拍立淘孵化出的搜索服务,提供低成本、高准确度、轻松交付的图像搜索服务!我们是基于阿里云IaaS底层,而且算法维护成本低,使用学习成本低,采用按月收费,同时 不限调用次数,不限调用次数,不限调用次数! 图像搜索是基于百亿级别的图片训练,我们算法毋庸置疑,尤其是在电商、零售领域是所向披靡。我们服务仅考虑客户图片总量大小和QPS数量,不做调用次数的收费,简单明了! 产品优势: • 搜索精度高:业内领先水平的搜索服务。 • 搜索效率高:结合超大规模聚类和量化索引技术,达到毫秒级响应。 • 支持海量数据:大规模检索引擎可支持百亿级别数据。 • 实时化接口:支持实时增删数据。 • 支持定制化:可根据用户需求定制化开发。 三.用户分析 我们的目标客户前提必须要有大量的图片,这些图片不仅可以是摄影作品,也可以是各种商品。 我们能为客户解决的核心需求就是:商品搜索、图片查找、近似图片推荐等。 如上图所示,我们所能覆盖的各种类目,电商类目是我们最擅长的,其他类目的话我们也有很多成熟的案例,也是完全可以商业化的。 在这要极其强调一点:如果有以上这些类目外的需求,比如说汽车,我们也可以去做一些探索和定制。 图搜场景例举: 电商图库类场景: 简化搜索流程,提升购买体验 用户只需拍摄或者上传商品照片,就可以根据图片进行商品搜索。 省去了繁琐的文字描述,简化了商品搜索流程,大大提升用户的购物体验。 通用图库类场景: 高性价比,支持大并发服务 图片分享和社交类网站通常有大量通用图片供用户搜索使用,利用图像搜索服务,可在云端快速构建百亿级图像搜索引擎,提供以图搜图功能,提升用户体验。 最后说一下: 我们使用图搜要有两个很重要的流程: 1.要建索引库。 客户要把所有图片离线上传,把图片库传到阿里云上面做一个特征的提取,提取之后我们会建立一个检索引擎。 2.在线流程。 当新的图片过来后,我们要做类目识别、主体检测、特征提取,然后和大库里面原来建好的索引做匹配。 以上就是我们今天想要告诉大家的主要内容,如果还是没有理解图像搜索真正的需求在哪里,可以先思考以下三个问题试试: 1. 客户能否使用准确文字来描述想要找的产品? 2. 客户如何区分想要的产品上细节层面的不同? 3. 海外客户使用不同语言怎么去描述需求? 原文链接 「深度学习福利」大神带你进阶工程师,立即查看>>> 2019 年是阿里云成立的第 10 年,在过去的 10 年间,阿里云引领了国内云基础设施市场;在当下,我们希望可以和您一起分享阿里云的新战略和新思考。 2019 年将是云原生在中国爆发和落地的一年,在这样的背景下,阿里将如何探索实时计算新架构?数据库的研发流程将如何演进?我们在代码智能化上又有怎样的思考和实践?云原生又将如何赋能AI、基因等各行业? 【开发者专场三大亮点】 亮点一 阿里巴巴集团副总裁/达摩院首席数据科学家/数据库产品事业部负责人 李飞飞 联合 阿里云云智能战略与合作部总经理 刘湘雯 共同畅谈开发者与可见的云未来。 亮点二 阿里云智能基础产品事业部研究员 周经森 发布 OpenJDK 长期支持版本 Alibaba Dragonwell 亮点三 重磅发布:CNCF X Alibaba 云原生技术课程 免费报名 https://www.yunqi.org/goPage?page=bj_signup_mb&activeId=2&ticketTypeId=39&channelId=23 【开发者专场议题】 原文链接 「深度学习福利」大神带你进阶工程师,立即查看>>> 本文由云+社区发表 作者:Java3y 前言 只有光头才能变强 没错,这篇主要跟大家一起 入门 机器学习。作为一个开发者,”人工智能“肯定是听过的。作为一个开发面试者,肯定也会见过”机器学习“这个岗位(反正我校招的时候就遇到过)。 可能还会听过或者见过“深度学习”、“神经网络”等等这些非常火的 名词 ,那你对这些 术语 了解多少呢? 相信大家这几天在朋友圈也可以看到这照片: 核心AI代码 // 通过if else 以人工穷举的方式来假装实现智能机器人聊天 希望阅读完本文中后,大家可以对这些术语和机器学习有一定的了解。 一、术语介绍 首先我们来简单看看人工智能、深度学习、机器学习这些术语和它们之间的关系究竟是怎么样的。 1.1人工智能 不知道听到“人工智能”大家会联想到什么,可能大多数都会想到科幻电影的机器人。 《人工智能》电影的剧照 我们看来看看维基百科的定义: 人工智能(英语:Artificial Intelligence,缩写为 AI)亦称机器智能,指由 人制造出来的机器所表现出来的智能 。通常人工智能是指通过普通计算机程序的手段实现的人类智能技术。 人工智能也可以分成两类: 强人工智能:强人工智能观点认为“有可能”制造出“真正”能推理(Reasoning)和解决问题的智能机器,并且,这样的机器将 被认为是具有知觉、有自我意识的 。 像绝大多数科幻电影中的机器人就是在这范畴 弱人工智能:弱人工智能观点认为“不可能”制造出能“真正”地推理和解决问题的智能机器,这些机器只不过 “看起来”像是智能 的,但是并不真正拥有智能,也不会有自主意识。 我们 目前阶段 的人工智能,其实都是弱人工智能。 1.2机器学习 不知道听到“机器学习”大家会联想到什么。Emmm…反正我就是从字面的意思去理解:“机器可以 自我 学习”。 首先我们看一下维基百科是怎么说的: 机器学习是实现人工智能的一个途径 ,即以机器学习为手段解决人工智能中的问题。机器学习在近30多年已发展为一门多领域 交叉学科 ,涉及概率论、统计学、逼近论、凸分析、计算复杂性理论等多门学科 简单来说:机器学习可以通过 大量的数据或者以往的经验自动改进计算机程序/算法 。 什么是机器学习 生成完 模型f(x) 之后,我们将 样例数据丢进模型里边 ,就可以输出结果: 输入样例进模型,输出结果 我们说机器学习可以 自我 学习,是因为我们会将 样例数据也会丢到“历史数据”中 ,这样生成模型就会有一定的改动,从而达到“自我学习”的效果。 1.3它们之间的关系 等等,我们好像还没讲深度学习呢。我们从上面机器学习的介绍也可以知道,机器学习已发展为一门多领域 交叉学科 ,机器学习中就有 好多个经典的算法,其中就包含了神经网络(深度学习可看成是神经网络的升级版) 。由于近几年深度学习发展迅猛,一些特有的学习手段相继被提出,所以越来越多的人将其单独看作一种学习的方法。 《机器学习 周志华》: 所谓深度学习,狭义地说就是**“很多层”的神经网络**,在若干测试和竞赛下,尤其涉及语音、图像等复杂对象的引用中,深度学习取得优越的性能。 所以我们可以总结出人工智能、机器学习、深度学习之间的关系是这样的: 机器学习,是实现人工智能的重要方法。 深度学习,是实现机器学习的技术。 之间的关系 想要了解更多,可参考: 人工智能、机器学习和深度学习的区别? https://www.zhihu.com/question/57770020 二、机器学习入门 通过上面我们可以简单认为机器学习就是: 利用计算机 从历史数据找出 规律 ,把这些规律用到 未来 不确定场景的决策中。 下面我们再来学习一下机器学习的一些入门知识。 2.1机器学习的术语 特征、样本、数据集、标记这些术语的说明: 特征、样本、数据集、标记这些术语的说明 特征(属性)所张成的空间叫做 特征空间 。 特征空间 例如我们把“色泽”、"根蒂“、”敲声“作为三个坐标轴,则它们张成一个用于描述 西瓜的三围空间 ,每个西瓜都可在这个空间中找到自己的坐标位置。 由于空间中的每个点对应一个坐标向量,我们也把一个示例称为“特征向量 ”。 特征向量 回到我们上面的图,再来讲讲“训练数据”、“训练”、“标记”: “训练数据”、“训练”、“标记”的术语解释 2.2机器学习的分类 一般机器学习又可以分成以下几类: 监督学习 半监督学习 非监督学习 增强学习 2.2.1监督学习 监督学习:训练数据(Training Data)可以告诉我们要找的那个模型的输入(Input)与输出(Output,也就是我们说的label)之间有什么样的关系。 给出的数据都有“答案”或“标记” 训练数据:"Java3y公众号"->好的公众号 , "Java4y公众号"->不好的公众号。 输出结果:好的公众号或者不好的公众号 在监听学习下又分为两种算法: 回归(Regression):结果是一个连续的 数值 (scalar),而非类别 分类(Classification):为训练数据进行分类别(多分类) 二分类:类别只有两种结果(YES OR NO) 回归例子 :知道前几天的PM2.5数值,预测一下明天的PM2.5数值。 回归例子 **二分类例子:**判断一封邮件是垃圾邮件还是正常邮件。 判断是垃圾邮件还是正常邮件 **多分类例子:**将新闻帖子分类成不同的类别。 分类成不同的类别 2.2.2非监督学习 非监督学习: 训练数据(Training Data)没有对应“答案”或“标记” 训练数据:"Java3y公众号" "Java4y公众号" "Java5y公众号" "Java6y公众号" "yyy公众号" "xxx公众号" "zzz公众号" 输出结果:("Java3y公众号" "Java4y公众号" "Java5y公众号" "Java6y公众号") ("yyy公众号" "xxx公众号" "zzz公众号") 分门类别 对没有“标记”的数据进行分类-聚类分析 对没有“标记”的数据进行分类-聚类分析 聚类分析例子: 在以前,中国移动有三个品牌:神州行、动感地带、全球通。我们给一堆的SIM卡交由学习算法训练, 不告诉它每张SIM卡具体是什么卡 ,最后我们是可以将这些SIM卡 分类别出来的 。 非监督学习的意义 非监督学习的意义 非监督学习的意义 2.2.3半监督学习 理解了监督学习和非监督学习,对于半监督学习就很容易理解了。 一部分数据有“标记”或者“答案”,另一部分数据没有 因为各种原因产生的标记缺失。 部分有label,部分没有label 通常都会使用非监督学习手段对数据进行处理(特征提取、降维),之后再只用监督学习手段做模型的训练和预测。 2.2.4增强学习 根据周围环境的情况,采取行动, 根据采取行动的结果,学习行动方式 增强学习 每次行动,就给这次的行动 评分 ,算法会根据评分来评估下一次的行动是好还是坏,最终不断改进。 给每次的行动评分 例子:Alpha Go下每步棋的时候都会 评估 自己这次下得怎么样,通过最终的结果不断改进下的每步棋。 2.3机器学习的其他分类 除了我们上面所说的监督学习、非监督学习、半监督学习、增强学习之外,机器学习也可以分成: 在线学习: 及时 将样例数据作为训练数据对模型进行训练。 需要加强对数据进行监控(有可能样本数据是脏数据,这样就破坏我们的模型) 离线(批量)学习: 定时 将样例数据作为训练数据对模型进行训练。 不能很快的适应环境的变化 还有: 参数学习:一旦学到了参数,就不再需要原有的数据集。通过调参数就好了。 非参数学习: 不对模型进行过多的假设 ,非参数不代表没参数。 最后 机器学习的核心在于算法上 ,这篇只是对机器学习的一个简单的入门,希望能对大家有所帮助。 此文已由腾讯云+社区在各渠道发布 获取更多新鲜技术干货,可以关注我们 腾讯云技术社区-云加社区官方号及知乎机构号 「深度学习福利」大神带你进阶工程师,立即查看>>> master分支 对于master分支,编译方法如下: git clone https://github.com/hankcs/HanLP.git mvn install -DskipTests ·由于目前一些test不够规范,使用了硬编码路径下的资源,所以暂时跳过单元测试。 ·该方法不会将src/main/resources目录下的任何资源(包括hanlp.properties)打包进jar,如果需要打包资源文件,请切换到portable分支。 portable分支 git checkout portable 然后将需要的data放入src/main/resources,最后执行: mvn install -DskipTests 运行jar 目前jar包中有一些模块可以命令行执行,以感知机词法分析器为例。由于这些模块一般需要加载外部data,所以需要在运行时指定hanlp.properties。运行时,一个典型的目录结构如下: ·此处只列出了重要的目录。 ·一个良好的实践是把hanlp.properties放到resources目录下。 命令行需要指定jar包和hanlp.properties所在的目录: $ java -cp target/hanlp-1.6.0.jar:src/main/resources com.hankcs.hanlp.model.perceptron.Main -test \ <<< '华安集团胡花蕊来到纽约艺术博物馆参观' [华安/nz 集团/n]/nt 胡花蕊/nr 来到/v [纽约/ns 艺术/n 博物馆/n]/ns 参观/v ·Windows用户请使用分号java -cp target/hanlp-1.6.0.jar;src/main/resources 「深度学习福利」大神带你进阶工程师,立即查看>>> tensorflow模型转snpe模型 如果没有做到下面的要求,模型转换时有可能会出现:SNPE支持的操作,却提示转换失败,而且很多。 生成tensorflow模型时用的tensorflow版本和运行snpe转换工具时的tensorflow版本要保持一致 生成tensorflow模型时用的python版本和运行snpe转换工具时的python版本要保持一致 同一层的节点要在同一个命名空间下 全连接层要有偏置 使用tensorboard去查看模型时,模型较小时,可以使用Chrome,模型较大时,可以使用火狐 「深度学习福利」大神带你进阶工程师,立即查看>>> 在“ 机器学习基础-假设空间、样本空间与归纳偏置 ”中提到了 归纳偏置实际上是一种模型选择策略, 尽管我们认为A模型更简单可能具有更好的泛化能力(更贴切实际问题对新数据的预测更准)而选择了A,但是实际情况中很可能会出现B模型比A更好的情况如图所示: (注:本文实际是对周志华西瓜书的部分总结) 黑点是训练数据,空心点是新数据,在(b)图中B模型比A模型更好。 也就是说在无数个模型中都可能会出现比A模型与实际数据更符合的情况(西瓜书中引入了NFL(没有免费的午餐定理)来着重说明 具体问题具体分析,这个具体问题实际上是指数据分布要与实际问题一致而不是指应用场景一致 ),换句话说哪个模型与实际情况更加符合我们就选择那个模型。 现在的问题是我们如何判断哪个模型与实际情况更加符合,因此引入了 模型的评估和选择 。 在评估和选择时,虽然使用了N种方法,但本质上还是将数据分成了训练集和测试集分别进行模型训练和模型验证,我们理想中的情况是训练集与测试集要同时与实际数据的概率分布一致,只有这样我们才能通过技术手段尽量选择到那个最优的模型,那N种方法直观上模型评估选择法,本质上是尽量保证与实际数据的概率分布一致! |