文档章节

现实比理论要复杂

我是任玉琢
 我是任玉琢
发布于 03/21 16:41
字数 2461
阅读 2.6K
收藏 1

我们试想一个实际问题,春天到了,我们要买衣服了,同时,作为服装厂商,也要开始发布新的衣服了,如果你作为一个服装厂商的技术顾问,请你分析出什么样的衣服属于今年的流行趋势,你会怎么做?

首先,作为技术宅男的你,我不认为你会对流行元素有那么多的关注,不会去看什么巴黎时装周,你能做的就是根据各种各样的数据进行分析预测。你可能会在店铺中进行一些扫码填写调查表发放一些优惠券,还可能去各大时尚网站去扒一些评论分析文章,还可能去微博这种公开的社交平台扒一些时尚博主的自拍分享等,你可以想尽办法获取各种各样的数据,你要做的是用好这些数据分析出流行趋势。

对于这种分析预测深度学习无疑是适合的模型工具,问题是既有这种调查表得来的数据,适合普通的层堆叠进行训练的模型,还有这种评论分析文章这种文本数据,适合循环神经网络的文章,还有各种图片需要分析,适合卷积神经网络的模型,这可怎么办?根据我们现有的知识,我们可以考虑分别训练不同的网络模型,对他们进行加权分析,这固然是一种方法,但是这个权也太随机了,各个数据之间也都割裂开了,分别处理算不上好的方法。那怎么办?

将他们联合起来,进行联合学习,大致的样子就是这样:

image

当然,联合模型不只能同时接收多种输入,还可以同时给出输出,比如流行的元素和价格,多个输入多个输出,这就不单纯是一个前面我们提到的各种线性的网络,更像是一个图的感觉,对,就是那种类似于数据结构中的图。就像数据结构中的图要比线性链表复杂得多一样,深度学习中的这种联合模型也比单一的一种模型要复杂得多。

函数式 API

这是预备知识,所谓函数式方式,很幸运 Keras 支持这样的方式,就是把层看成函数,这个函数的参数和返回值都是张量,这样的函数组合起来就能构成网络模型,看下面的代码例子:

def run():
    # 我们学过的用 Sequential 定义层
    seq_model = Sequential()
    seq_model.add(layers.Dense(32, activation='relu', input_shape=(64,)))
    seq_model.add(layers.Dense(32, activation='relu'))
    seq_model.add(layers.Dense(10, activation='softmax'))
    seq_model.summary()
​
    # 用函数的方式定义层
    input_tensor = Input(shape=(64,))
    x = layers.Dense(32, activation='relu')(input_tensor)
    x = layers.Dense(32, activation='relu')(x)
    output_tensor = layers.Dense(10, activation='softmax')(x)
    model = Model(input_tensor, output_tensor)
    model.summary()

这两种定义网络模型的方式是等价的,Input 是用来定义张量的,原始数据可以转换成张量用于网络训练,Model 是用来找到从 input_tensor 到 output_tensor 的路径,将其加入到网络中。

多输入模型

假设一个模型在进行一个简化版的图灵测试,这个模型要回答一个问题,这个问题的答案放在一篇文章中也由这个模型去处理,那么这个模型就是拥有多个输入的一个网络,如下:

image

其中有许多的知识我们之前讨论过了,如 LSTM 是循环神经网络,用于处理文本。这里的 Concatenate 需要解释一下:Concatenate 用于连接,可以将输入的问题和答案文本相关联起来。用代码构建的网络进行 summary 查看是这个样子的:

image

#!/usr/bin/env python3
​
import time
​
import keras
​import numpy as np
from keras import Input
from keras import layers
from keras.models import Model
​
​
def run():
    text_vocabulary_size = 10000
    question_vocabulary_size = 10000
    answer_vocabulary_size = 500
    # 文本输入
    text_input = Input(shape=(None,), dtype='int32', name='text')
    # 将输入转换为向量
    embedded_text = layers.Embedding(text_vocabulary_size, 64)(text_input)
    # 将输入转换为单个向量
    encoded_text = layers.LSTM(32)(embedded_text)
    # 对问题进行如上处理
    question_input = Input(shape=(None,), dtype='int32', name='question')
    embedded_question = layers.Embedding(question_vocabulary_size, 32)(question_input)
    encoded_question = layers.LSTM(16)(embedded_question)
​
    # 将问题和文本联系起来
    concatenated = layers.concatenate([encoded_text, encoded_question], axis=-1)
    # 添加一个 softmax 分类器
    answer = layers.Dense(answer_vocabulary_size, activation='softmax')(concatenated)
    # 构建模型
    model = Model([text_input, question_input], answer)
    model.summary()
​
    model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['acc'])
​
    # 生成虚拟训练数据,参考意义不大
    num_samples = 1000
    max_length = 100
    text = np.random.randint(1, text_vocabulary_size, size=(num_samples, max_length))
    question = np.random.randint(1, question_vocabulary_size, size=(num_samples, max_length))
    answers = np.random.randint(answer_vocabulary_size, size=(num_samples))
    answers = keras.utils.to_categorical(answers, answer_vocabulary_size)
    model.fit([text, question], answers, epochs=10, batch_size=128)
    model.fit({'text': text, 'question': question}, answers, epochs=10, batch_size=128)
​
​
if __name__ == "__main__":
    time_start = time.time()
    run()
    time_end = time.time()
    print('Time Used: ', time_end - time_start)

多输出模型

针对这个问题,实际用到的非常频繁,比如用户画像,我爸爸的百度首页就和我妈妈的不一样,虽然他们在设置里都没填写个人信息,但是还是知道他们分别喜欢什么,这是怎么做到的呢?

image

通过浏览的新闻,大致描述出用户的收入年龄等,再由后台对新闻的 Tag 标签,推送可能更感兴趣的新闻。注意这里的性别分类和收入分类就是不同的,一个是二分类,一个是多分类,这也需要定义不同的损失函数。想要完成这种任务,在对模型进行编译的时候,进行定义就是很好的办法。这里要注意,不平衡的损失贡献会影响模型的训练,如果某一项损失值特别大时,会导致模型会先对其进行优化而较少考虑或几乎不考虑其他任务的优化,这不是我们想看到的,因此我们还可以定义不同的损失对最终损失贡献的大小,进行损失加权:

image

def run():
    vocabulary_size = 50000
    num_income_groups = 10
    posts_input = Input(shape=(None,), dtype='int32', name='posts')
    embedded_posts = layers.Embedding(256, vocabulary_size)(posts_input)
    x = layers.Conv1D(128, 5, activation='relu')(embedded_posts)
    x = layers.MaxPooling1D(5)(x)
    x = layers.Conv1D(256, 5, activation='relu')(x)
    x = layers.Conv1D(256, 5, activation='relu')(x)
    x = layers.MaxPooling1D(5)(x)
    x = layers.Conv1D(256, 5, activation='relu')(x)
    x = layers.Conv1D(256, 5, activation='relu')(x)
    x = layers.GlobalMaxPooling1D()(x)
    x = layers.Dense(128, activation='relu')(x)
    age_prediction = layers.Dense(1, name='age')(x)
    income_prediction = layers.Dense(num_income_groups, activation='softmax', name='income')(x)
    gender_prediction = layers.Dense(1, activation='sigmoid', name='gender')(x)
    model = Model(posts_input, [age_prediction, income_prediction, gender_prediction])
    model.summary()
    
    # 关键在这里
    model.compile(optimizer='rmsprop',
                  loss={'age': 'mse',
                        'income': 'categorical_crossentropy',
                        'gender': 'binary_crossentropy'},
                  loss_weights={'age': 0.25,
                                'income': 1.,
                                'gender': 10.})
    # model.fit(posts, [age_targets, income_targets, gender_targets], epochs=10, batch_size=64)

图结构

当然,这里的图结构指的是有向无环图,层与层之间无环,层内部可以有环(循环神经网络),前文提到了,层就像函数,张量是输入也是输出,那么就可以由这些向量像搭积木一样组合复杂的网络图结构由上面多输入和多输出的例子,你应该也大致知道应该怎么组合了吧,这里给出两个非常著名的例子,如下:

Inception 模块

本模块有助于学习空间特征和每个通道的特征,这比联合学习这两种特征更加有效。本模型内置在 keras.applications.inception_v3.InceptionV3 中,对于处理 ImageNet 数据集有很好的效率和准确度。

image

branch_a = layers.Conv2D(128, 1, activation='relu', strides=2)(x)
branch_b = layers.Conv2D(128, 1, activation='relu')(x)
branch_b = layers.Conv2D(128, 3, activation='relu', strides=2)(branch_b)
branch_c = layers.AveragePooling2D(3, strides=2)(x)
branch_c = layers.Conv2D(128, 3, activation='relu')(branch_c)
branch_d = layers.Conv2D(128, 1, activation='relu')(x)
branch_d = layers.Conv2D(128, 3, activation='relu')(branch_d)
branch_d = layers.Conv2D(128, 3, activation='relu', strides=2)(branch_d)
output = layers.concatenate([branch_a, branch_b, branch_c, branch_d], axis=-1)

残差连接

本模块有助于解决梯度消失(反馈信号传递到更底部的层时,信号非常微弱)和表示瓶颈(某些层的参数过少,会导致某些特征永久消失,后面的层无法获取特征信息)的问题,具体的做法就是让前面某层的输出作为后面某层的输入:

image

x = ...
y = layers.Conv2D(128, 3, activation='relu', padding='same')(x)
y = layers.Conv2D(128, 3, activation='relu', padding='same')(y)
y = layers.Conv2D(128, 3, activation='relu', padding='same')(y)
y = layers.add([y, x])

共享层权重

函数的特点是复用,可以多次调用,这里的函数式编程,也是一样,一个层定义了一次,可以多次复用,这个很好理解:

# 定义一次
lstm = layers.LSTM(32)
left_input = Input(shape=(None, 128))
# 使用
left_output = lstm(left_input)
right_input = Input(shape=(None, 128))
# 使用
right_output = lstm(right_input)
merged = layers.concatenate([left_output, right_output], axis=-1)
predictions = layers.Dense(1, activation='sigmoid')(merged)
model = Model([left_input, right_input], predictions)

这是层的复用,扩大范围,模型也可以是输出向量,因此模型也是可以当做一个层的,这与前面文章的预定义网络有异曲同工之妙:

# Xception 是一个网络
xception_base = applications.Xception(weights=None, include_top=False)
left_input = Input(shape=(250, 250, 3))
​right_input = Input(shape=(250, 250, 3))
left_features = xception_base(left_input)
right_input = xception_base(right_input)
merged_features = layers.concatenate([left_features, right_input], axis=-1)

总结

本来我的每篇文章都控制在 1000 字左右,但是本篇文章实在是有点收不住了,他们之间的联系太紧密了,没办法,那就算一篇长文。其中的内容是一脉相承的,主要记住一点:因为这种函数式的 API,所以就可以构建复杂的网络结构,而不仅仅是单纯的网络层的堆叠,基于此,可以构建出多输入、多输出以及各种情况复杂的网络,并且因为这种函数式的特点,因此有各种复用的情况。

  • 本文首发自公众号:RAIS,欢迎关注!

© 著作权归作者所有

我是任玉琢
粉丝 10
博文 51
码字总数 65916
作品 0
东城
后端工程师
私信 提问
加载中

评论(0)

越写悦快乐之《精进》读书笔记之创造成功

关于作者 采铜,作家,2016年进入亚马逊中国年度新锐作家榜。他用手术刀一样的文字,剖析人们思维里的种种禁锢,将现实生活中的问题放于困惑的熔炉中反复冶炼,提炼出知识背后的知识、方法背...

韬声依旧在路上
2019/04/14
0
0
基于场景的学习,一个高度模拟真实世界的情形

看到前几天的话题后,相信有不少人会产生质疑,听起来像是在否定理论学习? 其实并不是。 恰恰相反,理论学习是一种非常高维的形式,正是它的高速发展促进了人类文明的进步。 大家经常会说知...

竹说
2019/03/18
0
0
2019人工智能学习路线图(怎么学好人工智能)

当前和未来两年,人工智能是技术行业的发展主流。用来开发机器学习主要有三门语言:Python Java C++,其中Python是主流。人工智能培训讲师哪个好?想必不用我强调,大家也清楚2019人工智能学...

QF _AI+python
2019/01/17
0
0
哪年你没有破坏一个你最爱的观念,那么这一年你就白过了

简单是长期努力工作的结果,而不是起点。 ——弗里德里克·迈特兰德 多元思维模型的复式框架 长久以来,我坚信存在某个系统,一个几乎所有聪明人都能掌握的系统,它比绝大多数人用的系统管用...

竹说
2019/01/25
0
0
火花四溅:AI与量子计算混合研究需继续深入

  【IT168 资讯】目前,我们正处于“量子争夺赛”中。谷歌IBM和全球研究人员在解决一些复杂的计算的时候,通常通过最先进的量子计算机来解决。   量子计算机与当今的家庭电脑非常相似只是...

it168网站
2017/10/24
0
0

没有更多内容

加载失败,请刷新页面

加载更多

leetcode1227(面试题 17.09. 第 k 个数)--C语言实现

求: 有些数的素因子只有 3,5,7,请设计一个算法找出第 k 个数。注意,不是必须有这些素因子,而是必须不包含其他的素因子。例如,前几个数按顺序应该是 1,3,5,7,9,15,21。 示例 1:...

拓拔北海
17分钟前
17
0
字节二面问我计算机网络的拥塞控制问题,清明节假,我终于搞明白了...

多点头发,少点代码 本文已经收录至我的GitHub,欢迎大家踊跃star 和 issues。 https://github.com/midou-tech/articles 本来想先更新TCP的基础和TCP可靠性等问题的,但是被你们暗示了,就先更...

龙跃十二
17分钟前
17
0
GIT 使用

创建问题 如果提示 'ssh-keygen' 不是内部或外部命令 1.需要安装git软件。 2.默认环境变量没有配置正确。 默认安装在Git\usr\bin目录下,通过cd命令进入该页面,然后执行命令即可。 常用命令...

奔跑的android
23分钟前
16
0
Centos8 配置静态IP

概述:在systemd还没有普及之前,重启服务都是/etc/init.d/xxx restart就可以了,包括网络服务!可是大概就是systemd普及的时候,我发现,用虚拟机安装的系统其网络服务都无法通过systemctl...

W_Lu
27分钟前
28
0
java 金钱元和分互相转换

import java.math.BigDecimal;import java.text.DecimalFormat;/** * @ProjectName: LieIdle * @Package: com.jinzhending.comm * @Author: huat * @Date: 2020/4/6 9:04 * ......

冥焱
31分钟前
11
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部