Android-InputManager分析

本篇为鸡生蛋系列第二篇文章 Linux input系统数据上报流程 https://segmentfault.com/a/11...Android InputManager分析主要讲一下inputmanager相关的,即驱动把数据上报到用户空间后,用户空间到应用这么个流程,在上一遍讲内核的input子系统时候,我们采取的反向分析,即由驱动出发,最后到input core,input子系统架构这么个由点到面的分析方法,那分析inputmanager是否可采用这种方法如何呢?实际上,对于Android上层(Native framework/framework, c++/java)的分析,我一般采用的是由上而下的分析,即从其初始化(main,构造,onFirstRef())开始, 通常在其初始化时候,会重一些很重要的上下层的连接,如果由下往上看,会麻烦点,然后再结合实例,看看他的数据流向是如何的,或者一些重要的API, 例如对于Audio来说,可以结合播放音乐流程来分析整个系统架构。 简单说来,input到应用的流程为EventHub监控并读取/dev/input下数据 --> 给InputReader 加工处理 --> 到InputDispacher --> 找到focused窗口并通过input channel发出去 参考文档:十分钟了解Android触摸事件原理(InputManagerService)https://www.jianshu.com/p/f05...android控件系统:输入事件在控件树中的传递https://blog.csdn.net/renshug...https://blog.csdn.net/renshug...InputManagerService分析一:IMS的启动与事件传递https://blog.csdn.net/lilian0... 相关代码目录:Android 9.0 http://androidxref.com/9.0.0_r3/frameworks/base/services/java/com/android/server/SystemServer.javaframeworks/base/services/core/java/com/android/server/input/InputManagerService.javaframeworks/base/services/core/jni/com_android_server_input_InputManagerService.cppframeworks/native/services/inputflinger/ 1.1 初始化frameworks/base/services/java/com/android/server/SystemServer.javastartOtherServices() { inputManager = new InputManagerService(context); .... wm = WindowManagerService.main(context, inputManager, ServiceManager.addService(Context.INPUT_SERVICE, inputManager, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL); .... inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); inputManager.start(); ......}IMS(InputManagerService)的初始化,是从SystemServer开始的,通过搜索代码(如上),我们可以看到构造了一个实例,并做为参数传给了WMS, 由此我们也猜想,会和WMS有紧密的关系,然后IMS设置了setWindowManagerCallbacks()并通过start()函数启动了,SystemServer里有关IMS的就这么几个地方,我们再看下构造和start()具体的流程,与WMS的关联不分析。 frameworks/base/services/core/java/com/android/server/input/InputManagerService.java// Pointer to native input manager service object.private final long mPtr;public InputManagerService(Context context) { this.mContext = context; this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper()); // config_useDevInputEventForAudioJack配置为true, 耳机事件可通过input上报 mUseDevInputEventForAudioJack = context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);...... mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());...... LocalServices.addService(InputManagerInternal.class, new LocalService());}public void start() { Slog.i(TAG, "Starting input manager"); nativeStart(mPtr);....}InputManagerService构造和start()主要也是调到JNI的 nativeInit() nativeStart(). ...

April 29, 2019 · 11 min · jiezi

表单系列之input number总结

各浏览器表现<input type=“number” />chrome除数字字符,只可输入e和.IE除数字字符,其他字符均可输入,无报错Firefox除数字字符,其他字符均可输入,但会报错移除箭头//谷歌去除箭头 input::-webkit-outer-spin-button,input::-webkit-inner-spin-button { -webkit-appearance: none;}//火狐去除箭头 input[type=“number”] { -moz-appearance: textfield;}maxlength无效<input type=“number” oninput=“if(value.length>5)value=value.slice(0,5)” /> step/min/max允许小数值<input type=“number” placeholder=“1.0” step=“0.01” min=“0” max=“10”>小知识点<label for=“ticketNum”>Number of tickets you would like to buy:</label><input id=“ticketNum” type=“number” name=“ticketNum” value=“0”>配合label for,点击label,就可让input获取焦点。

February 19, 2019 · 1 min · jiezi

Tensorflow分类器项目自定义数据读入

Tensorflow分类器项目自定义数据读入在照着Tensorflow官网的demo敲了一遍分类器项目的代码后,运行倒是成功了,结果也不错。但是最终还是要训练自己的数据,所以尝试准备加载自定义的数据,然而demo中只是出现了fashion_mnist.load_data()并没有详细的读取过程,随后我又找了些资料,把读取的过程记录在这里。首先提一下需要用到的模块:import osimport kerasimport matplotlib.pyplot as pltfrom PIL import Imagefrom keras.preprocessing.image import ImageDataGeneratorfrom sklearn.model_selection import train_test_split图片分类器项目,首先确定你要处理的图片分辨率将是多少,这里的例子为30像素:IMG_SIZE_X = 30IMG_SIZE_Y = 30其次确定你图片的方式目录:image_path = r’D:\Projects\ImageClassifier\data\set’path = “.\data”# 你也可以使用相对路径的方式# image_path =os.path.join(path, “set”)目录下的结构如下:相应的label.txt如下:动漫风景美女物语樱花接下来是接在labels.txt,如下:label_name = “labels.txt"label_path = os.path.join(path, label_name)class_names = np.loadtxt(label_path, type(”"))这里简便起见,直接利用了numpy的loadtxt函数直接加载。之后便是正式处理图片数据了,注释就写在里面了:re_load = Falsere_build = False# re_load = Truere_build = Truedata_name = “data.npz"data_path = os.path.join(path, data_name)model_name = “model.h5"model_path = os.path.join(path, model_name)count = 0# 这里判断是否存在序列化之后的数据,re_load是一个开关,是否强制重新处理,测试用,可以去除。if not os.path.exists(data_path) or re_load: labels = [] images = [] print(‘Handle images’) # 由于label.txt是和图片防止目录的分类目录一一对应的,即每个子目录的目录名就是labels.txt里的一个label,所以这里可以通过读取class_names的每一项去拼接path后读取 for index, name in enumerate(class_names): # 这里是拼接后的子目录path classpath = os.path.join(image_path, name) # 先判断一下是否是目录 if not os.path.isdir(classpath): continue # limit是测试时候用的这里可以去除 limit = 0 for image_name in os.listdir(classpath): if limit >= max_size: break # 这里是拼接后的待处理的图片path imagepath = os.path.join(classpath, image_name) count = count + 1 limit = limit + 1 # 利用Image打开图片 img = Image.open(imagepath) # 缩放到你最初确定要处理的图片分辨率大小 img = img.resize((IMG_SIZE_X, IMG_SIZE_Y)) # 转为灰度图片,这里彩色通道会干扰结果,并且会加大计算量 img = img.convert(“L”) # 转为numpy数组 img = np.array(img) # 由(30,30)转为(1,30,30)(即channels_first),当然你也可以转换为(30,30,1)(即channels_last)但为了之后预览处理后的图片方便这里采用了(1,30,30)的格式存放 img = np.reshape(img, (1, IMG_SIZE_X, IMG_SIZE_Y)) # 这里利用循环生成labels数据,其中存放的实际是class_names中对应元素的索引 labels.append([index]) # 添加到images中,最后统一处理 images.append(img) # 循环中一些状态的输出,可以去除 print(”{} class: {} {} limit: {} {}” .format(count, index + 1, class_names[index], limit, imagepath)) # 最后一次性将images和labels都转换成numpy数组 npy_data = np.array(images) npy_labels = np.array(labels) # 处理数据只需要一次,所以我们选择在这里利用numpy自带的方法将处理之后的数据序列化存储 np.savez(data_path, x=npy_data, y=npy_labels) print(“Save images by npz”)else: # 如果存在序列化号的数据,便直接读取,提高速度 npy_data = np.load(data_path)[“x”] npy_labels = np.load(data_path)[“y”] print(“Load images by npz”)image_data = npy_datalabels_data = npy_labels到了这里原始数据的加工预处理便已经完成,只需要最后一步,就和demo中fashion_mnist.load_data()返回的结果一样了。代码如下:# 最后一步就是将原始数据分成训练数据和测试数据train_images, test_images, train_labels, test_labels = \ train_test_split(image_data, labels_data, test_size=0.2, random_state=6)这里将相关信息打印的方法也附上:print("")print("%-28s %-s" % (“Name”, “Shape”))print("=================================================================")print("%-28s %-s" % (“Image Data”, image_data.shape))print("%-28s %-s" % (“Labels Data”, labels_data.shape))print("=================================================================")print(‘Split train and test data,p=%’)print("")print("%-28s %-s" % (“Name”, “Shape”))print("=================================================================")print("%-28s %-s" % (“Train Images”, train_images.shape))print("%-28s %-s" % (“Test Images”, test_images.shape))print("%-28s %-s" % (“Train Labels”, train_labels.shape))print("%-28s %-s" % (“Test Labels”, test_labels.shape))print("=================================================================")之后别忘了归一化哟:print(“Normalize images”)train_images = train_images / 255.0test_images = test_images / 255.0最后附上读取自定义数据的完整代码:import osimport kerasimport matplotlib.pyplot as pltfrom PIL import Imagefrom keras.layers import *from keras.models import *from keras.optimizers import Adamfrom keras.preprocessing.image import ImageDataGeneratorfrom sklearn.model_selection import train_test_splitos.environ[‘TF_CPP_MIN_LOG_LEVEL’] = ‘2’# 支持中文plt.rcParams[‘font.sans-serif’] = [‘SimHei’] # 用来正常显示中文标签plt.rcParams[‘axes.unicode_minus’] = False # 用来正常显示负号re_load = Falsere_build = False# re_load = Truere_build = Trueepochs = 50batch_size = 5count = 0max_size = 2000000000IMG_SIZE_X = 30IMG_SIZE_Y = 30np.random.seed(9277)image_path = r’D:\Projects\ImageClassifier\data\set’path = “.\data"data_name = “data.npz"data_path = os.path.join(path, data_name)model_name = “model.h5"model_path = os.path.join(path, model_name)label_name = “labels.txt"label_path = os.path.join(path, label_name)class_names = np.loadtxt(label_path, type(””))print(‘Load class names’)if not os.path.exists(data_path) or re_load: labels = [] images = [] print(‘Handle images’) for index, name in enumerate(class_names): classpath = os.path.join(image_path, name) if not os.path.isdir(classpath): continue limit = 0 for image_name in os.listdir(classpath): if limit >= max_size: break imagepath = os.path.join(classpath, image_name) count = count + 1 limit = limit + 1 img = Image.open(imagepath) img = img.resize((30, 30)) img = img.convert(“L”) img = np.array(img) img = np.reshape(img, (1, 30, 30)) # img = skimage.io.imread(imagepath, as_grey=True) # if img.shape[2] != 3: # print(”{} shape is {}".format(image_name, img.shape)) # continue # data = transform.resize(img, (IMG_SIZE_X, IMG_SIZE_Y)) labels.append([index]) images.append(img) print(”{} class: {} {} limit: {} {}" .format(count, index + 1, class_names[index], limit, imagepath)) npy_data = np.array(images) npy_labels = np.array(labels) np.savez(data_path, x=npy_data, y=npy_labels) print(“Save images by npz”)else: npy_data = np.load(data_path)[“x”] npy_labels = np.load(data_path)[“y”] print(“Load images by npz”)image_data = npy_datalabels_data = npy_labelsprint("")print("%-28s %-s" % (“Name”, “Shape”))print("=================================================================")print("%-28s %-s" % (“Image Data”, image_data.shape))print("%-28s %-s" % (“Labels Data”, labels_data.shape))print("=================================================================")train_images, test_images, train_labels, test_labels = \ train_test_split(image_data, labels_data, test_size=0.2, random_state=6)print(‘Split train and test data,p=%’)print("")print("%-28s %-s" % (“Name”, “Shape”))print("=================================================================")print("%-28s %-s" % (“Train Images”, train_images.shape))print("%-28s %-s" % (“Test Images”, test_images.shape))print("%-28s %-s" % (“Train Labels”, train_labels.shape))print("%-28s %-s" % (“Test Labels”, test_labels.shape))print("=================================================================")# 归一化# 我们将这些值缩小到 0 到 1 之间,然后将其馈送到神经网络模型。为此,将图像组件的数据类型从整数转换为浮点数,然后除以 255。以下是预处理图像的函数:# 务必要以相同的方式对训练集和测试集进行预处理:print(“Normalize images”)train_images = train_images / 255.0test_images = test_images / 255.0 ...

February 4, 2019 · 3 min · jiezi

开发函数计算的正确姿势——网页截图服务

前言首先介绍下在本文出现的几个比较重要的概念:函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息参考。Fun: Fun 是一个用于支持 Serverless 应用部署的工具,能帮助您便捷地管理函数计算、API 网关、日志服务等资源。它通过一个资源配置文件(template.yml),协助您进行开发、构建、部署操作。Fun 的更多文档参考。fun install: fun install 是 fun 工具的一个子命令,用于安装 pip 和 apt 依赖,提供了命令行接口和 fun.yml 描述文件两种形式。备注: 本文介绍的技巧需要 Fun 版本大于等于 2.9.3。依赖工具本项目是在 MacOS 下开发的,涉及到的工具是平台无关的,对于 Linux 和 Windows 桌面系统应该也同样适用。在开始本例之前请确保如下工具已经正确的安装,更新到最新版本,并进行正确的配置。DockerFunFcliFun 和 Fcli 工具依赖于 docker 来模拟本地环境。对于 MacOS 用户可以使用 homebrew 进行安装:brew cask install dockerbrew tap vangie/formulabrew install funbrew install fcliWindows 和 Linux 用户安装请参考:https://github.com/aliyun/fun/blob/master/docs/usage/installation.mdhttps://github.com/aliyun/fcli/releases安装好后,记得先执行 fun config 初始化一下配置。初始化使用 fun init 命令可以快捷的将本模板项目初始化到本地。$ fun init vangie/puppeteer-example? Please input oss bucket to upload chrome shell? chrome-headless? Please select a region? cn-hangzhou? Please input oss accessKeyId for upload? xxxxxxxxxxxKbBS? Please input oss accessKeySecret for upload? xxxxxxxxxxxx5ZgM上面会提示输入一个 OSS 的 BUCKET,注意 OSS Bucket 是全球唯一的,上面的 chrome-headless 已经被占用了,请换一个新的名称或者一个已经创建好的(已经创建好的,请确保 region 一致)。然后选择一个 OSS 的 Region,请保持和部署函数计算 Region 一致输入一个具备 OSS 写权限的秘钥。安装依赖$ fun installskip pulling image aliyunfc/runtime-nodejs8:build-1.2.0…Task => [UNNAMED] => apt-get update (if need) => apt-get install -y -d -o=dir::cache=/code/.fun/tmp libnss3 => bash -c ‘for f in $(ls /code/.fun/tmp/archives/*.deb); do dpkg -x $f /code/.fun/root; done;’ => bash -c ‘rm -rf /code/.fun/tmp/archives’Task => [UNNAMED] => bash -c ‘curl -L https://github.com/muxiangqiu/puppeteer-fc-starter-kit/raw/master/chrome/headless_shell.tar.gz –output headless_shell.tar.gz’…fun install 会执行 fun.yml 文件里的任务,这些任务会:安装 puppeteer 依赖的 .so 文件;将 puppeteer 依赖的 chrome headless 二进制文件上传到 OSS;安装 npm 依赖。部署$ fun deployusing region: cn-shanghaiusing accountId: ***********4733using accessKeyId: ***********KbBSusing timeout: 60Waiting for service puppeteer to be deployed… Waiting for function html2png to be deployed… Waiting for packaging function html2png code… package function html2png code done function html2png deploy successservice puppeteer deploy success执行$ fcli function invoke -s puppeteer -f html2pngThe screenshot has been uploaded to http://chrome-headless.oss-cn-shanghai.aliyuncs.com/screenshot.png打开上面的返回链接,看到截取出来的是全屏滚动的长图,考虑的篇幅下面只截取了部分:如果想换一个网址,可以使用如下命令格式fcli function invoke -s puppeteer -f html2png –event-str ‘http://www.alibaba.com’调试如果需要在本地调试代码,可以使用如下命令fun local invoke html2png <<<‘http://www.alibaba.com’参考阅读三分钟学会如何在函数计算中使用 puppeteer本文作者:倚贤阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

January 29, 2019 · 2 min · jiezi

GIF动画解析RNN,LSTM,GRU

摘要: 本文主要研究了维尼拉循环神经(RNN)、长短期记忆(LSTM)和门控循环单元(GRU)这三个网络,介绍的比较简短,适用于已经了解过这几个网络的读者阅读。循环神经网络是一类常用在序列数据上的人工神经网络。三种最常见的循环神经网络分别是:1.维尼拉循环神经网络(vanilla RNN)2.长短期记忆网络(LSTM),由Hochreiter和Schmidhuber于1997年提出3.门控循环单元网络(GRU),由Cho等人于2014年提出现在可以查到许多解释循环神经网络这一概念的图示。不过我个人比较推荐的是Michael Nguyen在《迈向数据科学》上发表的这篇文章,因为这篇文章撰写了关于这些模型的很多知识,而且提供了清楚易懂的插图,易于读者理解。这篇文章目的是启发大家思考如何更好地可视化这些单元中发生的情况,节点是如何共享的,以及它们怎么转换为输出节点这些问题。Michael 的精彩动画也给了我很大的启发,从中受益匪浅。本文主要研究了维尼拉循环神经(RNN)、长短期记忆(LSTM)和门控循环单元(GRU)这三个网络,介绍的比较简短,适用于已经了解过这几个网络的读者(并且建议在阅读本文之前阅读Michael的文章)。请读者注意,下面的动画是按顺序排列的,读者请依序查看。如下图所示,是我用来做插图的图例。在所演示的动画中,我使用了3(绿色)和2个隐藏单元(红色)的输入大小,批处理大小为1。演示如下:Vanilla RNN* t — time step 时间步长* X — input 输入* h — hidden state 隐藏状态* length of X — size/dimension of input X的长度表示输入的大小,尺寸* length of h — no. of hidden units. h的长度表示不属于隐蔽的单位注意,不同的库可以用不同的方式调用它们,但它们的含义都是相同的。Keras — state_size ,unitsPyTorch — hidden_sizeTensorFlow — num_unitsLSTM* C — cell state注意,单元格状态的维度与隐藏状态的维度相同。GRU希望这些动画片对你有所帮助!以下是静态图像中的单元格的概述:非常感谢德里克和任杰对本文的想法、建议和纠正。如果您想继续了解人工智能和深度学习,可以在Twitter@remykarem上阅读我写的关于这些的摘要文章和演示。本文作者:【方向】阅读原文本文为云栖社区原创内容,未经允许不得转载。

January 7, 2019 · 1 min · jiezi

(Python)零起步数学+神经网络入门

摘要: 手把手教你用(Python)零起步数学+神经网络入门!在这篇文章中,我们将在Python中从头开始了解用于构建具有各种层神经网络(完全连接,卷积等)的小型库中的机器学习和代码。最终,我们将能够写出如下内容:假设你对神经网络已经有一定的了解,这篇文章的目的不是解释为什么构建这些模型,而是要说明如何正确实现。逐层我们这里需要牢记整个框架:1. 将数据输入神经网络2. 在得出输出之前,数据从一层流向下一层3. 一旦得到输出,就可以计算出一个标量误差。4. 最后,可以通过相对于参数本身减去误差的导数来调整给定参数(权重或偏差)。5. 遍历整个过程。最重要的一步是第四步。 我们希望能够拥有任意数量的层,以及任何类型的层。 但是如果修改/添加/删除网络中的一个层,网络的输出将会改变,误差也将改变,误差相对于参数的导数也将改变。无论网络架构如何、激活函数如何、损失如何,都必须要能够计算导数。为了实现这一点,我们必须分别实现每一层。每个层应该实现什么我们可能构建的每一层(完全连接,卷积,最大化,丢失等)至少有两个共同点:输入和输出数据。现在重要的一部分假设给出一个层相对于其输出(∂E/∂Y)误差的导数,那么它必须能够提供相对于其输入(∂E/∂X)误差的导数。è®°ä½ï¼Eæ¯æ éï¼ä¸ä¸ªæ°å­ï¼ï¼XåYæ¯ç©éµã 我们可以使用链规则轻松计算∂E/∂X的元素:为什么是∂E/∂X?对于每一层,我们需要相对于其输入的误差导数,因为它将是相对于前一层输出的误差导数。这非常重要,这是理解反向传播的关键!在这之后,我们将能够立即从头开始编写深度卷积神经网络!花样图解基本上,对于前向传播,我们将输入数据提供给第一层,然后每层的输出成为下一层的输入,直到到达网络的末端。对于反向传播,我们只是简单使用链规则来获得需要的导数。这就是为什么每一层必须提供其输出相对于其输入的导数。这可能看起来很抽象,但是当我们将其应用于特定类型的层时,它将变得非常清楚。现在是编写第一个python类的好时机。抽象基类:Layer所有其它层将继承的抽象类Layer会处理简单属性,这些属性是输入,输出以及前向和反向方法。from abc import abstractmethod# Base classclass Layer: def init(self): self.input = None; self.output = None; self.input_shape = None; self.output_shape = None; # computes the output Y of a layer for a given input X @abstractmethod def forward_propagation(self, input): raise NotImplementedError # computes dE/dX for a given dE/dY (and update parameters if any) @abstractmethod def backward_propagation(self, output_error, learning_rate): raise NotImplementedError正如你所看到的,在back_propagation函数中,有一个我没有提到的参数,它是learning_rate。 此参数应该类似于更新策略或者在Keras中调用它的优化器,为了简单起见,我们只是通过学习率并使用梯度下降更新我们的参数。全连接层现在先定义并实现第一种类型的网络层:全连接层或FC层。FC层是最基本的网络层,因为每个输入神经元都连接到每个输出神经元。前向传播每个输出神经元的值由下式计算:使用矩阵,可以使用点积来计算每一个输出神经元的值:当完成前向传播之后,现在开始做反向传播。反向传播正如我们所说,假设我们有一个矩阵,其中包含与该层输出相关的误差导数(∂E/∂Y)。 我们需要:1.关于参数的误差导数(∂E/∂W,∂E/∂B)2.关于输入的误差导数(∂E/∂X)首先计算∂E/∂W,该矩阵应与W本身的大小相同:对于ixj,其中i是输入神经元的数量,j是输出神经元的数量。每个权重都需要一个梯度:使用前面提到的链规则,可以写出:那么:这就是更新权重的第一个公式!现在开始计算∂E/∂B:同样,∂E/∂B需要与B本身具有相同的大小,每个偏差一个梯度。 我们可以再次使用链规则:得出结论:现在已经得到∂E/∂W和∂E/∂B,我们留下∂E/∂X这是非常重要的,因为它将“作用”为之前层的∂E/∂Y。再次使用链规则:最后,我们可以写出整个矩阵:æä»¬å·²ç»å¾å°FC屿éçä¸ä¸ªå ¬å¼ï¼ 编码全连接层现在我们可以用Python编写实现:from layer import Layerimport numpy as np# inherit from base class Layerclass FCLayer(Layer): # input_shape = (1,i) i the number of input neurons # output_shape = (1,j) j the number of output neurons def init(self, input_shape, output_shape): self.input_shape = input_shape; self.output_shape = output_shape; self.weights = np.random.rand(input_shape[1], output_shape[1]) - 0.5; self.bias = np.random.rand(1, output_shape[1]) - 0.5; # returns output for a given input def forward_propagation(self, input): self.input = input; self.output = np.dot(self.input, self.weights) + self.bias; return self.output; # computes dE/dW, dE/dB for a given output_error=dE/dY. Returns input_error=dE/dX. def backward_propagation(self, output_error, learning_rate): input_error = np.dot(output_error, self.weights.T); dWeights = np.dot(self.input.T, output_error); # dBias = output_error # update parameters self.weights -= learning_rate * dWeights; self.bias -= learning_rate * output_error; return input_error;激活层到目前为止所做的计算都完全是线性的。用这种模型学习是没有希望的,需要通过将非线性函数应用于某些层的输出来为模型添加非线性。现在我们需要为这种新类型的层(激活层)重做整个过程!不用担心,因为此时没有可学习的参数,过程会快点,只需要计算∂E/∂X。我们将f和f’分别称为激活函数及其导数。前向传播正如将看到的,它非常简单。对于给定的输入X,输出是关于每个X元素的激活函数,这意味着输入和输出具有相同的大小。反向传播给出∂E/∂Y,需要计算∂E/∂X注意,这里我们使用两个矩阵之间的每个元素乘法(而在上面的公式中,它是一个点积)编码实现激活层激活层的代码非常简单:from layer import Layer# inherit from base class Layerclass ActivationLayer(Layer): # input_shape = (1,i) i the number of input neurons def init(self, input_shape, activation, activation_prime): self.input_shape = input_shape; self.output_shape = input_shape; self.activation = activation; self.activation_prime = activation_prime; # returns the activated input def forward_propagation(self, input): self.input = input; self.output = self.activation(self.input); return self.output; # Returns input_error=dE/dX for a given output_error=dE/dY. # learning_rate is not used because there is no “learnable” parameters. def backward_propagation(self, output_error, learning_rate): return self.activation_prime(self.input) * output_error;可以在单独的文件中编写一些激活函数以及它们的导数,稍后将使用它们构建ActivationLayer:import numpy as np# activation function and its derivativedef tanh(x): return np.tanh(x);def tanh_prime(x): return 1-np.tanh(x)*2;损失函数到目前为止,对于给定的层,我们假设给出了∂E/∂Y(由下一层给出)。但是最后一层怎么得到∂E/∂Y?我们通过简单地手动给出最后一层的∂E/∂Y,它取决于我们如何定义误差。网络的误差由自己定义,该误差衡量网络对给定输入数据的好坏程度。有许多方法可以定义误差,其中一种最常见的叫做MSE - Mean Squared Error:其中y 和y分别表示期望的输出和实际输出。你可以将损失视为最后一层,它将所有输出神经元吸收并将它们压成一个神经元。与其他每一层一样,需要定义∂E/∂Y。除了现在,我们终于得到E!以下是两个python函数,可以将它们放在一个单独的文件中,将在构建网络时使用。import numpy as np# loss function and its derivativedef mse(y_true, y_pred): return np.mean(np.power(y_true-y_pred, 2));def mse_prime(y_true, y_pred): return 2(y_pred-y_true)/y_true.size;网络类到现在几乎完成了!我们将构建一个Network类来创建神经网络,非常容易,类似于第一张图片!我注释了代码的每一部分,如果你掌握了前面的步骤,那么理解它应该不会太复杂。from layer import Layerclass Network: def init(self): self.layers = []; self.loss = None; self.loss_prime = None; # add layer to network def add(self, layer): self.layers.append(layer); # set loss to use def use(self, loss, loss_prime): self.loss = loss; self.loss_prime = loss_prime; # predict output for given input def predict(self, input): # sample dimension first samples = len(input); result = []; # run network over all samples for i in range(samples): # forward propagation output = input[i]; for layer in self.layers: # output of layer l is input of layer l+1 output = layer.forward_propagation(output); result.append(output); return result; # train the network def fit(self, x_train, y_train, epochs, learning_rate): # sample dimension first samples = len(x_train); # training loop for i in range(epochs): err = 0; for j in range(samples): # forward propagation output = x_train[j]; for layer in self.layers: output = layer.forward_propagation(output); # compute loss (for display purpose only) err += self.loss(y_train[j], output); # backward propagation error = self.loss_prime(y_train[j], output); # loop from end of network to beginning for layer in reversed(self.layers): # backpropagate dE error = layer.backward_propagation(error, learning_rate); # calculate average error on all samples err /= samples; print(’epoch %d/%d error=%f’ % (i+1,epochs,err));构建一个神经网络最后!我们可以使用我们的类来创建一个包含任意数量层的神经网络!为了简单起见,我将向你展示如何构建……一个XOR。from network import Networkfrom fc_layer import FCLayerfrom activation_layer import ActivationLayerfrom losses import from activations import import numpy as np# training datax_train = np.array([[[0,0]], [[0,1]], [[1,0]], [[1,1]]]);y_train = np.array([[[0]], [[1]], [[1]], [[0]]]);# networknet = Network();net.add(FCLayer((1,2), (1,3)));net.add(ActivationLayer((1,3), tanh, tanh_prime));net.add(FCLayer((1,3), (1,1)));net.add(ActivationLayer((1,1), tanh, tanh_prime));# trainnet.use(mse, mse_prime);net.fit(x_train, y_train, epochs=1000, learning_rate=0.1);# testout = net.predict(x_train);print(out);同样,我认为不需要强调很多事情,只需要仔细训练数据,应该能够先获得样本维度。例如,对于xor问题,样式应为(4,1,2)。结果$ python xor.py epoch 1/1000 error=0.322980 epoch 2/1000 error=0.311174 epoch 3/1000 error=0.307195 … epoch 998/1000 error=0.000243 epoch 999/1000 error=0.000242 epoch 1000/1000 error=0.000242 [array([[ 0.00077435]]), array([[ 0.97760742]]), array([[ 0.97847793]]), array([[-0.00131305]])]卷积层这篇文章开始很长,所以我不会描述实现卷积层的所有步骤。但是,这是我做的一个实现:from layer import Layerfrom scipy import signalimport numpy as np# inherit from base class Layer# This convolutional layer is always with stride 1class ConvLayer(Layer): # input_shape = (i,j,d) # kernel_shape = (m,n) # layer_depth = output depth def init(self, input_shape, kernel_shape, layer_depth): self.input_shape = input_shape; self.input_depth = input_shape[2]; self.kernel_shape = kernel_shape; self.layer_depth = layer_depth; self.output_shape = (input_shape[0]-kernel_shape[0]+1, input_shape[1]-kernel_shape[1]+1, layer_depth); self.weights = np.random.rand(kernel_shape[0], kernel_shape[1], self.input_depth, layer_depth) - 0.5; self.bias = np.random.rand(layer_depth) - 0.5; # returns output for a given input def forward_propagation(self, input): self.input = input; self.output = np.zeros(self.output_shape); for k in range(self.layer_depth): for d in range(self.input_depth): self.output[:,:,k] += signal.correlate2d(self.input[:,:,d], self.weights[:,:,d,k], ‘valid’) + self.bias[k]; return self.output; # computes dE/dW, dE/dB for a given output_error=dE/dY. Returns input_error=dE/dX. def backward_propagation(self, output_error, learning_rate): in_error = np.zeros(self.input_shape); dWeights = np.zeros((self.kernel_shape[0], self.kernel_shape[1], self.input_depth, self.layer_depth)); dBias = np.zeros(self.layer_depth); for k in range(self.layer_depth): for d in range(self.input_depth): in_error[:,:,d] += signal.convolve2d(output_error[:,:,k], self.weights[:,:,d,k], ‘full’); dWeights[:,:,d,k] = signal.correlate2d(self.input[:,:,d], output_error[:,:,k], ‘valid’); dBias[k] = self.layer_depth * np.sum(output_error[:,:,k]); self.weights -= learning_ratedWeights; self.bias -= learning_ratedBias; return in_error;它背后的数学实际上并不复杂!这是一篇很好的文章,你可以找到∂E/∂W,∂E/∂B和∂E/∂X的解释和计算。如果你想验证你的理解是否正确,请尝试自己实现一些网络层,如MaxPooling,Flatten或DropoutGitHub库你可以在GitHub库中找到用于该文章的完整代码。本文作者:【方向】阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

December 21, 2018 · 4 min · jiezi