ANN原来如此简单!——用Excel实现的MNIST手写数字识别(之二)

2023-10-22 02:40

本文主要是介绍ANN原来如此简单!——用Excel实现的MNIST手写数字识别(之二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

ANN原来如此简单

人工神经网络目前仍然是一个火热的话题,许多人都对它充满了兴趣。然而,对于想了解ANN具体是怎么回事的同学来说,往往缺乏一个足够简单可视化的方法去了解神经网络的内部构造。网络上的各种文章除了数学公式以外,剩下的多是利用Tensorflow等现成的python库实现的样例,虽然实现起来简单,但是底层函数全部封装起来,十分难以理解。因此,神经网络往往在大家的心目中呈现出高深莫测的样子。然而,这篇文章的写作目的就是为了把神经网络拉下神坛,让任何对Excel有最基本了解的人也能在自己的电脑上搭建出一个能准确识别手写数字(准确率达到95%以上)的神经网络出来,同时还能对神经网络的运行底层算法有一个非常直观的了解。对于仅仅是感兴趣的初学者来说,能够非常直观地看到神经元长什么样子,能够非常清晰地看到网络的前馈计算和反向传播的参数传递过程,对于希望了解神经忘咯具体算法的研究者或爱好者来说,能够直观地了解各种不同的算法参数的区别,从而对神经网络各个超参数(hyper parameter)的选择和调整产生更深刻的理解。

本文在写作时考虑到需要照顾各个不同层次的读者,因此会力求从浅到深,按照下面的结构来编排,同时每一个章节又相对独立,如果对某些部分已经熟悉的读者,大可跳过一部分,直接阅读感兴趣的章节即可

  • 第一节,神经网络的历史
    神经网络和人工智能
    人工神经网络是一个很老的概念
    神经元和神经系统
  • 第二节,开始了解人工神经网络
    感知器模型和多层感知器模型
    神经网络的前向传播
    神经网络的有监督学习和反向传播
  • 第三节,Excel中实现MNIST手写数字识别
    MNIST数据集
    神经元基本计算的实现
    损失函数及反向传播算法的实现
    用简单的宏实现随机梯度下降训练
    训练成果的
  • 第四节,神经网络的优化:实现CNN卷积网络
    卷积网络的结构
    Excel中逐步实现卷积池化和全联接计算
    随机梯度下降算法及训练成果
  • 第五节,更多的内容
    使用VBA实现的ANN Excel工具箱
    使用tensorflow实现ANN

第二节,开始了解人工神经网络

人工神经网络是近年来非常火热的概念,作为实现人工智能的工具之一最近以来受到了非同寻常的关注和瞩目。

人工神经网络是机器学习算法的一种,而机器学习算法有被认为是实现人工智能的途径之一。我们现在看到的很多人工智能的成果,从自动下棋程序到机器视觉,自然语言处理、自动驾驶、数据挖掘,无一不是基于机器学习的算法来实现的。

机器学习算法通过程序的“学习”来提高其性能。按照机器学习的思想,为了让计算机程序能够完成一项任务 T T ,我们必须定义一个明确的性能指标P,同时为这个程序设计一种获取经验 E E 的方法1,比如:

手写识别学习问题
任务T:识别并分类图像中的手写文字
性能指标 P P :分类的正确率
训练经验E:一组手写文字的图片,并且已经做好标记

人工神经网络作为机器学习算法,当然也是遵循上面的范式,不过,性能指标 P P 的取值并不是分类正确率,而是一个表示错误大小的“损失函数(Cost Function)”,这一点我们在下文中会看到。

感知器和多层感知器模型

感知器模型
人脑最基本的功能性组成单元是单个的神经细胞,通常也被称为神经元,神经元在大脑内相互连接,组成一个网络状的结构,彼此之间通过微弱的脉冲电位互相通信。人们相信,通过模拟神经元的功能,并且模拟某种特定的神经元网络结构,就能模拟出(至少是部分)人脑的功能。关于神经元的更多介绍,可以看这里。
神经元组成神经网络

感知器(Perceptron)就是这样一个人工神经元的数学模型,它最早由McCullen和Pitts在1943年提出,因此被称为MP感知器,通过简单的数学形式描述了神经元的三大特性:单向信号传递,多输入单输出以及连接强度可变。这个数学模型是这样的:

这里写图片描述

神经元的输入
如上图中所示,一个感知器能够接受N个实数值作为输入,这 N N 个实数输入分别记为x1,x2,x3,,xN,并且通常写成向量的形式,我们叫它输入向量:

X={x1,x2,x3,,xN}T X = { x 1 , x 2 , x 3 , … , x N } T

这个输入向量模拟了神经元的树突电信号输入。就如同人眼视网膜的光学感受器会把光线转化为电信号,亮度越高的地方电信号就越强,反之亦然,电信号通过视网膜细胞的轴突传递到下一层神经细胞的树突上。每一个神经细胞的树突能够接受若干个其他神经元的输出。同样的道理,机器视觉的输入是一幅图像,这幅图像中的每一个像素的颜色值就代表了输入的强度,一个神经元可以连接全部像素,也可以连接部分像素,把所有输入像素值表示为一个向量 X X ,就成为这个神经元的输入值。

神经元的权值
人脑神经元接受到输入电位后,如果输入电位的总和大于某一个阈值,这个神经元就会被“激活”从而产生一个电脉冲,并通过它的轴突传递到下一个神经元。在感知器模型中,同样的状况也会发生:感知器在接受到输入向量X后,会把输入向量的所有 N N 个分量累加起来,但为了模拟神经元中每个树突连接的强度不同这个事实,通常会给每一个输入xi先乘以一个实数 wi w i 之后再累加起来,这个实数 wi w i 实际上起到了一个对单个输入进行调节的作用,类似于神经元的树突强度,如果某个树突节的连接强度很强,那么输入的信号就会被加强,反之,某个树突的输入就会被削弱。这样,如果一个神经元的树突连接强度发生了变化,最终就会引起这个神经元对输入产生完全不同的兴奋反应。

由于树突的连接强度会影响神经元的反应,因此它可以说是神经元最重要的参数之一,在感知器模型中,这个参数被称为“权值weights”,借鉴了加权累加的概念。感知器权值的数量与输入的数量一样,都是N个: w1,w2,w3,,wN w 1 , w 2 , w 3 , … , w N ,同样也可以写成向量的形式:

W={w1,w2,w3,,wN}T W = { w 1 , w 2 , w 3 , … , w N } T
所有的输入与权值相乘并累加的形式就可以写成图中的形式,并且可以用向量乘积的形式表示:
i=1Nxiwi=XTW ∑ i = 1 N x i w i = X T W

神经元的激活,偏置
一个神经元的激活与否除了取决于输入的电信号强度、树突连接强度之外,还取决于神经元本身的兴奋阈值,就像我们听一个笑话的时候,有些人笑点很低会立即哈哈大笑,而另一些人由于笑点太高而觉得一点都不好笑一样,只有当输入的电信号强度超过了一个神经元的“笑点”时,它才会被“激活”从而产生电信号传递给下一个神经元。为了模拟这个特性,MP感知器是这样设置的:只有当输入加权总和大于某一个实数值时,感知器的输入就为1(感知器被激活),否则就为0(不激活)。为了方便地描述这个阈值,McCullen和Pitts给感知器设定了第二个重要参数 b b ,这个参数被称为“偏置bias”,并且规定感知器的输出Out为:

Out={0,XTW+b01,XTW+b>0 O u t = { 0 , X T W + b ≤ 0 1 , X T W + b > 0

上面的式子便是经典MP感知器的数学公式了。要使这个感知器能够学习,就是通过选择合适的权值 w w 和偏置b,使得这个感知器在特定输入的时候输出1,而在其他时候输出0。比如,我们现在有一个双输入感知器,我们只要这样选择 w w b,它就能成为一个逻辑与(AND)门2:

b=0.8w1=w2=0.5 b = 0.8 w 1 = w 2 = 0.5

可以验证,只有当输入 x1 x 1 x2 x 2 都为1时,感知器的输出才为1,同样,我们改变一下感知器的 w w b值,也能让它成为一个逻辑或(OR)门3,当 x1 x 1 x2 x 2 都为0时,感知器输出才为0:

b=0.3w1=w2=0.5 b = 0.3 w 1 = w 2 = 0.5

有意思的是,XOR函数是无法用单个感知器来表示的4,要表示XOR函数,需要两个感知器并联后与一个感知器串联形成的双层网络才能实现。感兴趣的读者可以自己试着推算一下什么样的参数组合可以表达XOR函数(本节最后有答案)

从现在的观点来看,与其他试图模拟思维逻辑的人工智能方法相比,感知器模型最大的特点就是抛弃了可理解的逻辑,把解决问题的方法最终归结为搜索和优化——按照传统的方法,解决一个问题必须首先告诉机器解决这个问题的逻辑步骤,但是感知器方法却不需要这样,你需要解决一个问题,不用告诉我逻辑步骤,只要在整个参数的可能取值范围中”搜索“到正确的取值就可以了。这种思路对于大多数模糊问题简直是天大福音:大家想想,怎么教机器通过一系列逻辑步骤去识别一个数字”2“?你可以告诉它”2像一个小鸭子“,如此等等,但是具体到真实的像素,一个手写的数字2存在着巨量的例外要素,根本无法通过逻辑步骤来描述,如下图5。而用搜索优化的思路,则意味着我们只要找到正确的函数表达形式,那么总有一组最优化的参数让这个函数给出正确的结果——至于这个函数优化后人类是否能理解,则不在我们的考虑范围之内了。
这里写图片描述

由于本质上使用感知器表达的问题可以用搜索最优值的方法来求解,因此各种最优值搜索算法都可以被用上了,但是对于一个稍微大一些的感知器来说,由于搜索的空间巨大,因此最实用的算法是一种递进算法,比如从某一个可能的解开始,采用小步递进的方法来逐步逼近最优解。一个很形象的比喻是爬山:假设我们的目标是爬到一座山的最顶峰,但是不知道山顶的具体方位,如何爬到山顶呢?我们可以从山区的任何一点作为起点,然后一步步地移动,只要通过某种方法保证我没走一步都是往海拔更高的方向走一步,那么最后就一定能够爬到山顶。这就是所谓的爬山法:
这里写图片描述

假设我们需要设计一个感知器,它的性能 P P 取决于w b b 参数的组合,需要找到一组参数wmax bmax b m a x 以使 P P 达到最优Pmax

求解最优性能 P P 的位置坐标(对应的参数)可以采用爬山法:假设我们只有两个参数,形成一组位置坐标,我们将性能P想象成每个位置坐标的海拔高度,那么爬山法就等效于在地图上找到山顶( P P 最大值Pmax所在的点)的位置坐标。如上图,在不知道山顶(感知器的最优性能表现 Pmax P m a x )坐标( w w b的具体参数)的情况下,从图中的第一个任意小红点坐标(一个任意 w w b参数组合)出发一步步逼近目标,每一步都往山坡最陡峭( P P 相对于w b b 参数的变化率最大)的方向走一步,最终走到山顶的位置(找到wmax bmax b m a x )。除了爬山法之外,我们还可以采用粒子群算法(particle swamp algorithm)遗传算法等寻优算法,同样可以达到我们的目标。

但是为了使用爬山法。MP感知器却是无法工作的,因为它缺乏一项关键的特性:可微分性。由于MP感知器的输出函数是不连续的二值函数,这样导致我们无法爬山。想象一下,我们寻找山顶的方法是依据脚下山坡的斜率,我们只管往斜度最大的方向往上爬就可以了,但是如果我们要爬的山是一座平原上的平顶山,而我们的起点恰好又选在了平坦的平原上,没有斜率的信息可以利用,我们如何找到山顶呢?如下图:
这里写图片描述

答案是将二值函数替换为一个平滑可微分的函数,就像下面的图像一样:
这里写图片描述

两座山仍然有相似的边界,但是平滑的山坡让我们终于能够利用脚下的斜率了。我们用来平滑感知器输出的函数通常是一个输出域有限,如 1<out<1 − 1 < o u t < 1 的函数,同时应该单调上升并且处处可微分,这类函数画出来往往呈S形,我们把它们叫做 Sigmoid S i g m o i d 函数,最典型的Sigmoid函数叫做Logistics函数,它是这样的一个函数:

Out(y)=11+ey O u t ( y ) = 1 1 + e − y
这个函数的图像如下:
这里写图片描述如果我们令:
y=XTW y = X T W
,那么就得到了一种新的平滑的感知器:
Out(y)=11+eXTW O u t ( y ) = 1 1 + e − X T W
我们把它叫做Sigmoid感知器,或者Sigmoid函数

Sigmoid函数之所以被选中,不仅仅因为它具备了单调上升处处可微的特点,还因为它有一个非常宝贵的性质,在下一小节中我们会重点讨论。

多层感知器
早在感知器诞生之初人们就知道,单个感知器可以表示的函数非常有限,连。然而,早已有人证明一个由足够多感知器组成的多层网络,可以在任意精度上逼近任意连续函数6

多层感知器是由感知器组成的多层网络,如下图所示:
这里写图片描述

一个典型的多层网络是由感知器组成的结构,以上图为例,这个网络输入层(Input)有三个节点,代表网络的输入参数有三个(注意输入层并没有感知器),由四个感知器组成了隐藏层(Hidden),其中每个感知器都与输入层连接,并产生一个输出,连接到输出层(Output),输出层有两个感知器,接受隐藏层的输出作为其输入,并产生两个输出,作为网络的输出。注意,图中的输出层与隐藏层并不是全联接的,上面感知器与前三个隐藏层感知器连接,而下方的感知器与下面两个隐藏层感知器连接。事实上,在一个多层网络中,网络的连接方式可以有多种多样的,除了部分连接、全联接之外,跨层连接甚至成环连接都可以。在过去的几十年里,人们研究过了许多种不同的网络结构,用于完成不同的任务。

我们现在讨论的,是最简单的全联接多层感知器网络。就像下图中一样,每一层的感知器都与前一层的所有感知器直接连接,且不存在其他多余的连接。
全联接网络

神经网络的前向传播

到了这里,我们就需要正式进入神经网络的世界,而不仅仅是单个感知器了。为了符合习惯,从这里开始我都会用“神经元”或者“单元”来指代神经网络中的感知器。

对于神经网络来说,反向传播算法可以说是最重要的一项发明。在反向传播算法发明以前,由于缺乏对多层网络的有效训练方法,而单层网络的表征能力又非常之差,导致神经网络在60~70年代经历了一次低谷,直到80年代反向传播发明之后7,神经网络才得以再次复兴

神经网络的前向传播

我们经常把神经网络叫做前馈网络(Feed Forward Network, FF),这是因为神经网络的输出计算过程是一个前向传播的过程。这个过程可以用前面的例图来形象地说明:
全联接网络

在前向传播的过程中,整个网络的参数是静态不变的,所有的计算从第一层输入层开始,将 x1 x 1 x2 x 2 x3 x 3 作为输入参数输入网络后,向前传递到隐藏层的四个神经元,每一个神经元都会把这三个输入参数作为它自己的输入,计算其输出,假设 outhj o u t j h 代表隐藏层第i个单元的输出,这个输出的计算可以按前面介绍的感知器输出公式计算(假设这个单元是Sigmoid单元,且有 N N 个输入)

outjh=Logistics(i=1Nxiwij+bj)
整个隐藏层的输出向量 Oh O h 可以写成

Oh=Logistics(XTWj+bj) O h = L o g i s t i c s ( X T W j + b j )
其中
Logistics(a)=11+ea L o g i s t i c s ( a ) = 1 1 + e − a
wij w i j 表示这个神经元(第 j j 个神经元)的第i个权值,与第 i i 个输入xi相配。

接下来输出层的计算方式与隐藏层一样,但是两个输出单元的输入来自于隐藏层(它的前一层单元)的输出。假设 outk o u t k 代表输出层第 k k 个单元的输出,隐藏层有M个单元,它的计算公式如下:

outk=Logistics(j=1Mouthjwjk+bk) o u t k = L o g i s t i c s ( ∑ j = 1 M o u t j h w j k + b k )
整个网络的输出O可以写成
O=Logistics(OhTWk+bk) O = L o g i s t i c s ( O h T W k + b k )
其中
Logistics(a)=11+ea L o g i s t i c s ( a ) = 1 1 + e − a

这样我们就得到了网络的输出值,输出值的数量与输出单元的数量一致,两个输出单元输出两个实数 out1,out2 o u t 1 , o u t 2

通过神经网络的前向传播,我们把一组输入信号(可能是图像、视频、音频、文字或者任何类型的信号,只要这些信号以一种有规律的形式编排好即可)从神经网络(由若干神经元前后连接而成)的输入层进入,产生第一层输出信号,继而传递至下一层神经元产生下一层输出信号,直到产生的信号传递到最末一层输出层,在这里产生对我们而言有意义的输出信号。这个过程被称为信号的“前向传播”Feed Forward Propagation。前向传播就是神经网络根据输入信号产生相应信号的过程,犹如我们的眼睛看到了一个人,视网膜上的视神经将视觉信号传递到我们大脑的视觉皮层区域,经过短短几十毫秒的信息传递之后,我们的大脑就能迅速判断出这个人是成龙。

用于对图像进行分类的神经网络也能实现类似的功能,比如在2012年出现的AlexNet中,它的输出层有1000个神经元,每一个神经元代表一个明确且互不重复的分类,比如,第一个神经元代表一朵花,而第二个神经元代表一只猫,以此类推。由于采用了特殊的输出函数,这1000个神经元输出的实数和永远为1,因此,我们就可以把这1000个神经元的输出理解为一种分类的置信度。比如当第一个神经元输出为0.02,第二个神经元输出为0.98时,代表神经网络认为一幅图片为一朵花的置信度为0.02,而认为它是一只猫的置信度为0.98.

通过前向传播,神经网络能够对输入信号作出反应,但是,它并不能对网络产生任何影响,无法改变神经网络内部神经元的连接强度权值,所以,前向传播是无法让神经网络产生学习能力的,如果一个神经网络错误地把一个数字3识别成了“9”,那么就算我们重复一万次地把数字3输入到神经元中进行前向传播,这个网络仍然无法学习到正确的分类值。

如果要让神经网络“学会”它还没有掌握的知识或技能,就需要反向传播算法的帮助了。

神经网络的有监督学习——反向传播

神经网络的“学习”在机器学习领域属于“有监督学习”算法。有监督学习特别像我们小时候老师教我们学习识字的过程:老师先将几个字写在黑板上,告诉我们每个字的读音,然后让我们重复,如果我们读音不正确,则反复不断地教授,慢慢地我们的大脑就能调整神经反射,到最后我们就能正确地读出这些字。

神经网络的学习也是一样:为了让神经网络“学习”到正确的知识,我们必须反复地把“正确的知识”传授给神经网络,以便它能逐渐地调整自己“大脑”中的神经元连接强度,最终“学到”正确的知识。比如,我们如果想让神经网络学习识别手写数字,就必须同时告诉它正确的答案:
这里写图片描述

因此,在“学习”的过程中,我们必须同时给网络提供两方面的输入,一方面我们需要把图像输入到网络的输入层,并且使用前向传播让网络产生输出;另一方面,我们还需要“告诉”网络正确的输出,以便网络进行自我调整。想象一下小学生的课堂授课:老师在黑板上写下了一个大大的“山”字,问小明“这个字念什么呀?”。如果小明读对了,那么老师就会夸奖一句并让小明坐下,而如果小明读错了,那么老师就会把正确的读音告诉小明,并且提醒小明记住。在训练神经网络的时候,我们也会像老师一样,让网络自行产生一个输出后,会告诉网络这个输出的正确与否。不过,与老师直接给出正确答案不同,我们会采用更加“数学”的方法——我们会告诉网络这个输出与正确答案之间的“误差值”。“误差值”可以理解为网络输出与正确答案之间相差的离谱程度——网络输出与正确答案差的越离谱,误差值就越大,相反误差值就越小。因此,误差值就是网络输出 o o 与正确答案t之间偏差的一个函数 C(o) C ( o ) ,也被称为“误差函数”,通常最常见的误差函数被称为均方差函数:

C(o)=12i=1N(oiti)2 C ( o ) = 1 2 ∑ i = 1 N ( o i − t i ) 2

知道了网络输出的误差之后,就可以把误差用于网络的训练了。在网络的学习或训练过程中,我们是通过“反向传播算法”来实现权值的调整的

本节中的小问题的答案
表达XOR函数的双层网络8:
这里写图片描述

<未完待续>


  1. Tom M. Mitchell: Machine Learning,机器学习第一章,引言,曾华军,张银奎等译 ↩
  2. Tom M. Mitchell: Machine Learning,机器学习第四章,人工神经网络,曾华军,张银奎等译 ↩
  3. Tom M. Mitchell: Machine Learning,机器学习第四章,人工神经网络,曾华军,张银奎等译 ↩
  4. Minsky & Papert (1969) ↩
  5. Machine Learning and artificial intelligence ↩
  6. 来源于Weierstrass定理(Weierstrass,1885)的一个自然推论,由Cybenko于1988年发表论文严格证明 ↩
  7. Rumelhart & McClelland 1986, Parker 1985 ↩
  8. (图片来自文档网,作者不明) https://www.wendangwang.com/doc/a27facce48908060e024b9ae/3 ↩

这篇关于ANN原来如此简单!——用Excel实现的MNIST手写数字识别(之二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/258565

相关文章

从去中心化到智能化:Web3如何与AI共同塑造数字生态

在数字时代的演进中,Web3和人工智能(AI)正成为塑造未来互联网的两大核心力量。Web3的去中心化理念与AI的智能化技术,正相互交织,共同推动数字生态的变革。本文将探讨Web3与AI的融合如何改变数字世界,并展望这一新兴组合如何重塑我们的在线体验。 Web3的去中心化愿景 Web3代表了互联网的第三代发展,它基于去中心化的区块链技术,旨在创建一个开放、透明且用户主导的数字生态。不同于传统

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

hdu2289(简单二分)

虽说是简单二分,但是我还是wa死了  题意:已知圆台的体积,求高度 首先要知道圆台体积怎么求:设上下底的半径分别为r1,r2,高为h,V = PI*(r1*r1+r1*r2+r2*r2)*h/3 然后以h进行二分 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#includ

阿里开源语音识别SenseVoiceWindows环境部署

SenseVoice介绍 SenseVoice 专注于高精度多语言语音识别、情感辨识和音频事件检测多语言识别: 采用超过 40 万小时数据训练,支持超过 50 种语言,识别效果上优于 Whisper 模型。富文本识别:具备优秀的情感识别,能够在测试数据上达到和超过目前最佳情感识别模型的效果。支持声音事件检测能力,支持音乐、掌声、笑声、哭声、咳嗽、喷嚏等多种常见人机交互事件进行检测。高效推

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

usaco 1.3 Prime Cryptarithm(简单哈希表暴搜剪枝)

思路: 1. 用一个 hash[ ] 数组存放输入的数字,令 hash[ tmp ]=1 。 2. 一个自定义函数 check( ) ,检查各位是否为输入的数字。 3. 暴搜。第一行数从 100到999,第二行数从 10到99。 4. 剪枝。 代码: /*ID: who jayLANG: C++TASK: crypt1*/#include<stdio.h>bool h