现实比理论要复杂
我们试想一个实际问题,春天到了,我们要买衣服了,同时,作为服装厂商,也要开始发布新的衣服了,如果你作为一个服装厂商的技术顾问,请你分析出什么样的衣服属于今年的流行趋势,你会怎么做?
首先,作为技术宅男的你,我不认为你会对流行元素有那么多的关注,不会去看什么巴黎时装周,你能做的就是根据各种各样的数据进行分析预测。你可能会在店铺中进行一些扫码填写调查表发放一些优惠券,还可能去各大时尚网站去扒一些评论分析文章,还可能去微博这种公开的社交平台扒一些时尚博主的自拍分享等,你可以想尽办法获取各种各样的数据,你要做的是用好这些数据分析出流行趋势。
对于这种分析预测深度学习无疑是适合的模型工具,问题是既有这种调查表得来的数据,适合普通的层堆叠进行训练的模型,还有这种评论分析文章这种文本数据,适合循环神经网络的文章,还有各种图片需要分析,适合卷积神经网络的模型,这可怎么办?根据我们现有的知识,我们可以考虑分别训练不同的网络模型,对他们进行加权分析,这固然是一种方法,但是这个权也太随机了,各个数据之间也都割裂开了,分别处理算不上好的方法。那怎么办?
将他们联合起来,进行联合学习,大致的样子就是这样:
当然,联合模型不只能同时接收多种输入,还可以同时给出输出,比如流行的元素和价格,多个输入多个输出,这就不单纯是一个前面我们提到的各种线性的网络,更像是一个图的感觉,对,就是那种类似于数据结构中的图。就像数据结构中的图要比线性链表复杂得多一样,深度学习中的这种联合模型也比单一的一种模型要复杂得多。
函数式 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 的路径,将其加入到网络中。
多输入模型
假设一个模型在进行一个简化版的图灵测试,这个模型要回答一个问题,这个问题的答案放在一篇文章中也由这个模型去处理,那么这个模型就是拥有多个输入的一个网络,如下:
其中有许多的知识我们之前讨论过了,如 LSTM 是循环神经网络,用于处理文本。这里的 Concatenate 需要解释一下:Concatenate 用于连接,可以将输入的问题和答案文本相关联起来。用代码构建的网络进行 summary 查看是这个样子的:
#!/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)
多输出模型
针对这个问题,实际用到的非常频繁,比如用户画像,我爸爸的百度首页就和我妈妈的不一样,虽然他们在设置里都没填写个人信息,但是还是知道他们分别喜欢什么,这是怎么做到的呢?
通过浏览的新闻,大致描述出用户的收入年龄等,再由后台对新闻的 Tag 标签,推送可能更感兴趣的新闻。注意这里的性别分类和收入分类就是不同的,一个是二分类,一个是多分类,这也需要定义不同的损失函数。想要完成这种任务,在对模型进行编译的时候,进行定义就是很好的办法。这里要注意,不平衡的损失贡献会影响模型的训练,如果某一项损失值特别大时,会导致模型会先对其进行优化而较少考虑或几乎不考虑其他任务的优化,这不是我们想看到的,因此我们还可以定义不同的损失对最终损失贡献的大小,进行损失加权:
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 数据集有很好的效率和准确度。
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)
残差连接
本模块有助于解决梯度消失(反馈信号传递到更底部的层时,信号非常微弱)和表示瓶颈(某些层的参数过少,会导致某些特征永久消失,后面的层无法获取特征信息)的问题,具体的做法就是让前面某层的输出作为后面某层的输入:
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,欢迎关注!
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
03月23日云栖号头条:阿里巴巴包机运送540万只口罩抵达非洲支持54国抗疫
云栖号:https://yqh.aliyun.com第一手的上云资讯,不同行业精选的上云企业案例库,基于众多成功案例萃取而成的最佳实践,助力您上云决策! 今日最新云头条快讯: 北京时间3月22日14:45,埃塞俄比亚首都亚的斯亚贝巴,历时10小时飞行,满载抗疫物资的埃航ET3751包机货运航班抵达博莱机场,将540万只口罩、108万试剂盒、4万套防护服和6万个防护面罩运达非洲;新加坡公共服务部门的内部IT机构新加坡政府科技局与卫生部合作,宣布推出名为TraceTogether的移动应用程序,以帮助支持新型冠状病毒感染接触者的追踪工作,从而帮助阻止病毒传播。 一起来看最新的资讯: 阿里巴巴包机运送540万只口罩抵达非洲支持54国抗疫 据了解,这批包机物资由阿里菜鸟全球救援绿色生命线紧急发往埃塞俄比亚。埃塞俄比亚航空公司一直没有间断中国的航班,优先安排货机运力和最专业的机组,广州员工加班加点协调,保障航班连夜奔赴非洲。埃塞俄比亚总理阿比联合联合国粮食计划署、非洲疾病预防控制中心将负责在非洲的物流和分发工作。“疫情正在全世界蔓延,我们必须跑在病毒前面。非洲的医疗资源相对缺乏,一旦蔓延,后果不...
- 下一篇
并发编程的艺术03-Bakery互斥锁算法
导读 本章会介绍Bakery互斥锁算法,涉及到并发下的公平性问题,有界计数器和无界计数器问题,存储单元数量下界问题。 公平性 无饥饿特性能够保证每一个调用 lock() 函数的线程最终都将进入临界区,但并不能保证进入临界区需要多长时间。理想情况下如果线程 A 在线程 B 之前调用 lock() 函数,那么 A 应该在 B 之前进入临界区。然而,运用现有的工具无法确定那个线程首先调用 lock() 函数。取而代之的做法是,将 lock() 函数代码划分为两个部分: 1. 门廊区: 其执行区间由有限个操作步组成。 2. 等待区: 其执行区间可能包扩无穷个操作步。 门廊区应该在有限步数内完成一种强约束条件。称这种约束为有界无等待演进特性。对于公平的定义:满足下面条件的锁称为先到先服务的:如果线程 A 门廊区的结束在线程 B 门廊区的开始之前完成,那么线程 A 比定不会被线程 B 赶超。 按照我们的惯例来举一个生活中的例子来帮助读者理解这种计算机术语都抽象描述。 大多数人都去银行办理过业务,如图1所示很多人都在等待,他们等待的依据是什么呢?总得有个先来后到吧,要不然有人插队岂不是...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS关闭SELinux安全模块
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Hadoop3单机部署,实现最简伪集群
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- CentOS7安装Docker,走上虚拟化容器引擎之路