首页 文章 精选 留言 我的

精选列表

搜索[文智公众趋势分析],共10000篇文章
优秀的个人博客,低调大师

智能家居如何“智” 自动到智能步步维艰

现在我们谈智能家居的时候,很多时候其实是在谈遥控。现在一些做智能家居或者智能家电的企业,将能够用手机操控作为智能的概念大肆向消费者兜售,或者将自动的概念引申为智能。这对我们消费者而言不是一件好事,对我们的生产厂商而言其实也不是好事。 作为一名普通的消费者,对这些概念应该有一个基本的认识,这样才不会受到炒作的影响从而产生被欺骗的感觉。从长远来看,对我们的生产厂商而言,不做误导消费者的事才有利于行业的健康发展。 国外机构的一份调查报告显示,2015年上半年,用户对智能家居产品的兴趣下降了15%,低于去年同期的水平。我们可以看到,“智能”这个概念对我们消费者而言已经逐渐失去吸引力了。这背后一部分原因来自消费者接受较慢,另一部分来自厂商对消费者的误导。 我认为,我们的智能产品的生产厂商对此要负很大责任。按照我们厂商的说法,经常被提及的有两个原因:首先是曾经的老生常谈,说是行业标准没有制定好,做起来困难。其次是说市场不成熟,推出某些产品是为培养用户习惯。 但在我看来,这里面的借口成分居多,这里以我们说的一些智能家居产品为例。有些商家对智能家居的理解有偏差,或是受限于技术,或是受限于认知,推出了一些并不智能的产品,并得意地打上“智能”的标签后卖给消费者。比较典型的是“智能按钮”的设置:一键开衣柜,一键开窗帘,一键开浴室门……再配上一些灯具和床,一套智能家居基本就出来了。做得好一些的将按键也省去了,变作辅助动力系统,用户轻轻推或拉一下,剩下的就由动力系统自己完成了。看上去家居似乎是变得聪明了,有智能了。 当然,这里面进步是实实在在的,我们不能否认这些技术带来生活的便利,做事情不能一蹴而就,一步步稳扎稳打才是对自己和消费者负责。然而,就是这么一套家居,普通人家基本不会买,会购买这种方案的基本都是家住别墅的有钱人。一个很重要的原因是这样做对用户的成本很高,而体验很一般。 这一部分商家固然是行业的先驱,为了对用户进行市场教育而义无反顾投身这个行业,但如果没做好,就可能从先驱变成先烈了。我们也不排除部分投机分子,借着智能的概念,大肆炒作,把消费者玩坏了。这部分人对行业并不理解,或者装作不理解。 事实上,正是这批把市场玩坏的人,最终被市场教育了。这种为了“智能”而智能的例子不胜枚举,在此不展开了。虽然一口气吃个胖子很难,但是胡乱吃药,可能就会没病吃出病来。 我们现在的创业者是有些浮躁的。脑子里有了创意,就凑几个做硬件开发的、做通信的、做软件开发的,搞几场头脑风暴,抓抓市场需求,然后做套差不多的原型机出来就开始找投资人讲故事去了,还生怕自己的创意被人抢先了。这样做,很多时候根本没想清楚什么是“智能”,就开始搞智能家居、智能硬件的开发了。 这样做出来的很多是“伪智能”,伪智能体现在很多方面,其中很重要一点是伪需求。举个简单例子,用户真的需要用手机去控制家里的各个家居和家电吗?这一点很明显不对。把手机作为控制中心其实只是延续了移动互联网时代人们的操作习惯而已,并不是我们常挂在嘴边的“智能”。 怎么区分“自动”和“智能”呢?之前我讲的智能空调和Nest的区别可以作为很好的例证。从遥控到无控是一条艰难而缓慢的道路,在这条路上的我们一定要有清醒的认识。最后能被称为“智能家居”的,都是不需要人去操控的。智能家居未来将会配置更多的传感器,这些传感器采集到的数据,其用途将不仅仅限于智能家居系统本身,更可以和智能社区、智慧城市联动起来,而在这个过程中,对用户本身几乎不造成任何影响。这才是“智能”的形态。 另外,现在的智能产品还涉及信息的私密性问题,这也是现在很多用户对智能家居缺乏兴趣的重要原因。这也是“智能”带来的新问题,缺乏“智能”的产品没有这方面的担忧。用户将有很多数据被采集,而这些被采集的数据将被用在何处,用户自己也不知道。这不仅是监管的问题,而且需要有效的技术手段来规避风险。 接下来就是合理地使用数据的问题,数据仅仅在手机上使用绝对和“智能”俩字不搭边。当两个智能化物品能够通信的时候,它们之间交换的信息在不经过云端的情况下,彼此就能很好地识别和运用才是实现真正的智能的重要方法。这里面固然有标准未统一的问题,但同样也是一个认知的问题。现在,我们很多数据都还只是停留在采集阶段。比如智能手环、手表等采集了我们的运动量等信息,但并没有为我们提供更多有效的关于运动的服务。一些朋友望着屏幕上的一堆图标和数据,觉得索然无味。 造成这样结果的原因,很重要的一点就是物联网的产品太少,形成联动的机会不多。而现在的商家缺乏创新,总是跟在别人后面亦步亦趋,造成市面上用户能买到的智能商品品类有限,同质化严重。而真正能影响用户需求的应用却很少被开发出来,更重要的是一旦被开发出来,很快会被跟风者们抄袭模仿,这也是创新者们最头疼的问题。 这不是一时半会儿能解决的问题,还需要所有从业者从规范自己开始,形成良好的行业道德标准。否则智能家居或智能硬件行业,有可能如同奶粉、快餐、食用油等行业充满了行业内的“潜规则”。由此,我们不仅需要技术上的标准,更需要从行业准则方面来限制从业人员的不良行为。 总的说来,智能产品的生产者正确认识“智能”是第一步,之后才能谈得上市场培养。否则,产生的结果就是误导消费者,给同行带来压力,同时自身发展极大受限。 作为消费者而言,识别真正具有“智能”的产品会为生活带来很多便利和良好的体验,这里面并没有简单而通行的方法。但随着时间的推移,那些“伪智能”的产品将逐渐失去市场,而真正好的产品将逐步获得更高的市场占有率。 我对此还是比较乐观的。这场物联网的风暴,就像以往的其他技术变革一样,会以一种“倒逼”的方式到来。举个简单例子,现在我们在市场上买不到黑白电视,就是所有的电视生产厂家都不会去做过时的事情。同样,我们的手机更多以智能手机为主,因为生产功能手机的厂商大大减少,你去买基本都是智能手机了。同样的道理,未来的很多产品的智能化进程会与此类似,当技术成熟之后,生产厂商提供给消费者的将都是真正具有智能的产品,而不会刻意去强调“智能”了。到时候,我们的选择就变得简单而实用了。 本文转自d1net(转载)

优秀的个人博客,低调大师

一文带你了解容器探针

本文分享自华为云社区《Kubernetes探针原理介绍》,作者: 可以交个朋友。 简介 容器探针(Container Probes)是一种机制,由 kubelet 对容器执行定期的探查,从而获取容器的状态。探针的类型有三种: 启动探针(Startup Probe) 存活探针(Liveness Probe) 就绪探针(Readiness Probe) 探针功能 启动探针 启动探针(StartupProbe)主要用于检测容器内的应用是否已经成功启动并完成初始化任务。它的主要作用有以下几点: 延缓其他探针生效: 在容器启动初期,启动探针先于存活探针(LivenessProbe)和就绪探针(ReadinessProbe)生效。当启动探针配置存在时,kubelet 不会执行存活和就绪探针,直到启动探针成功为止。这对于某些启动时间较长或者启动过程中有复杂初始化序列的应用程序来说非常重要,可以避免在应用还未完全启动时就被误判为不健康或就绪,进而被错误地重启或流量过早涌入。 防止频繁重启: 若应用启动期间,存活探针或就绪探针就开始工作,而此时应用可能还没有完全启动成功,这两个探针可能会因为应用未能及时响应而触发容器重启,造成不必要的服务中断和循环重启。启动探针的存在可以有效地防止此类情况的发生。 存活探针 存活探针(Liveness Probe)主要作用是检测容器内主进程或服务是否仍然运行正常且响应健康检查。具体来说: 自动恢复: 当存活探针检测失败时,kubelet 将认为该容器内的主进程已经不再健康或者已停止提供预期的服务。此时,kubelet 会根据 Pod 的重启策略来决定是否应该重新启动这个容器。通过这种方式,存活探针可以帮助实现故障自愈,及时恢复服务的可用性。 就绪探针 就绪探针(Readiness Probe)主要作用是检测容器是否已经准备好对外提供服务。具体来说: 流量路由控制: 当就绪探针成功时,表示该容器内部的应用程序已处于可接受请求的状态,此时 kubelet 会将该容器标记为“就绪”状态,Service 将会将其 IP 地址添加到后端服务列表中,允许 Service 开始将网络流量转发至这个 Pod。 避免无效请求: 如果就绪探针失败,则意味着容器可能还在启动过程中、正在重启服务、或者由于某种原因暂时无法正常响应请求。在这种情况下,kubelet 会将容器从 Service 的后端池中移除,确保不会向其发送任何用户请求,从而避免了因应用未准备完毕而引起的错误响应和用户体验下降。 平滑过渡: 通过就绪探针,Kubernetes 可以实现滚动更新或部署过程中的平滑过渡,新版本的容器在通过就绪探针验证前,不会承担任何实际流量,直到它们完全启动并做好处理请求的准备。 探针方式 探针实现方式有三种: HTTP GET请求: Kubernetes 通过向容器内应用发送一个HTTP GET请求来检查应用的状态。如果收到的 HTTP 响应码在 200-399 范围内,则认为该探测成功。 livenessProbe: #可指定其他两种探针类型 httpGet: #指定探针方式 path: /healthz #http请求路径 port: 8080 #请求端口 httpHeaders: # 可选,用于设置自定义HTTP头部 - name: Custom-Header value: huawei TCP Socket检查: Kubernetes 尝试与容器上指定的端口建立 TCP 连接。如果能够成功建立连接,则说明探测成功。 livenessProbe: tcpSocket: port: 8080 exec执行命令: 在容器内部执行一个命令,并根据命令退出时返回的状态码判断容器是否正常运行。通常情况下,如果命令返回 0,则表示成功。 livenessProbe: exec: command: - cat - /tmp/health 启动探针、存活探针和就绪探针同时支持这三种方式。每种探针可以选择不同探测方式 探针配置参数 Kubernetes中的探针都支持一些通用的参数来定义它们的行为。以下是这些探针通常使用的配置参数: initialDelaySeconds:容器启动后要等待多少秒后才启动启动、存活和就绪探针。 如果定义了启动探针,则存活探针和就绪探针的延迟将在启动探针已成功之后才开始计算。 如果 periodSeconds 的值大于 initialDelaySeconds,则 initialDelaySeconds 将被忽略。默认是 0 秒,最小值是 0。 periodSeconds:执行探测的时间间隔(单位是秒)。默认是 10 秒。最小值是 1。 timeoutSeconds:探测的超时后等待多少秒。默认值是 1 秒。最小值是 1。 successThreshold:探针在失败后,被视为成功的最小连续成功数。默认值是 1。存活和启动探针的这个值必须是 1。 failureThreshold:探针连续失败了 failureThreshold 次之后, Kubernetes 认为总体上检查已失败:容器状态未就绪、不健康、不活跃。 对于启动探针或存活探针而言,如果至少有 failureThreshold 个探针已失败, Kubernetes 会将容器视为不健康并为这个特定的容器触发重启操作。 kubelet 遵循该容器的terminationGracePeriodSeconds 设置。 terminationGracePeriodSeconds:k8s1.25以上版本新增,为 kubelet 配置从为失败的容器触发终止操作到强制容器运行时停止该容器之前等待的宽限时长。 默认值是继承 Pod 级别的 terminationGracePeriodSeconds 值(如果不设置则为 30 秒),最小值为 1。就绪探针不需要配置该参数 完整配置示例: livenessProbe: #可以选择 httpGet、tcpSocket 或 exec 中的一种 httpGet: path: /health port: 8080 httpHeaders: - name: Custom-Header value: huawei #通用参数: initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 1 successThreshold: 1 failureThreshold: 3 terminationGracePeriodSeconds: 30 readinessProbe: # 就绪探针配置类似 startupProbe: # 启动探针配置也相似 配置建议 如果应用是慢启动类型,建议配置启动探针或者为存活探针配置initialDelaySeconds参数,避免存活探针过早介入导致容器频繁重启。如果应用启动时间不固定建议使用启动探针。 可以将启动探针initialDelaySeconds、periodSeconds的值调低,让启动探针更快感知容器健康状态;由于启动探针探测成功后就会退出不会影响容器后续运行,可以将failureThreshold的值调大,避免应用还未启动完成过早触发重启 由于存活探针探测失败会导致容器重启,因此将存活探针的failureThreshold设置比就绪探针大,这样如果应用有问题应该先切断流量 点击关注,第一时间了解华为云新鲜技术~

优秀的个人博客,低调大师

一文讲透 Redis 事务

准确的讲,Redis 事务包含两种模式 : 事务模式 和 Lua 脚本。 先说结论: Redis 的事务模式具备如下特点: 保证隔离性; 无法保证持久性; 具备了一定的原子性,但不支持回滚; 一致性的概念有分歧,假设在一致性的核心是约束的语意下,Redis 的事务可以保证一致性。 但 Lua 脚本更具备实用场景,它是另一种形式的事务,他具备一定的原子性,但脚本报错的情况下,事务并不会回滚。Lua 脚本可以保证隔离性,而且可以完美的支持后面的步骤依赖前面步骤的结果。 Lua 脚本模式的身影几乎无处不在,比如分布式锁、延迟队列、抢红包等场景。 1 事务原理 Redis 的事务包含如下命令: 序号 命令及描述 1 MULTI 标记一个事务块的开始。 2 EXEC 执行所有事务块内的命令。 3 DISCARD 取消事务,放弃执行事务块内的所有命令。 4 WATCH key [key ...] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。 5 UNWATCH 取消 WATCH 命令对所有 key 的监视。 事务包含三个阶段: 事务开启,使用 MULTI , 该命令标志着执行该命令的客户端从非事务状态切换至事务状态 ; 命令入队,MULTI 开启事务之后,客户端的命令并不会被立即执行,而是放入一个事务队列 ; 执行事务或者丢弃。如果收到 EXEC 的命令,事务队列里的命令将会被执行 ,如果是 DISCARD 则事务被丢弃。 下面展示一个事务的例子。 redis> MULTI OK redis> SET msg "hello world" QUEUED redis> GET msg QUEUED redis> EXEC 1) OK 1) hello world 这里有一个疑问?在开启事务的时候,Redis key 可以被修改吗? 在事务执行 EXEC 命令之前 ,Redis key 依然可以被修改。 在事务开启之前,我们可以 watch 命令监听 Redis key 。在事务执行之前,我们修改 key 值 ,事务执行失败,返回 nil 。 通过上面的例子,watch 命令可以实现类似乐观锁的效果 。 2 事务的ACID 2.1 原子性 原子性是指:一个事务中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。 第一个例子: 在执行 EXEC 命令前,客户端发送的操作命令错误,比如:语法错误或者使用了不存在的命令。 redis> MULTI OK redis> SET msg "other msg" QUEUED redis> wrongcommand ### 故意写错误的命令 (error) ERR unknown command 'wrongcommand' redis> EXEC (error) EXECABORT Transaction discarded because of previous errors. redis> GET msg "hello world" 在这个例子中,我们使用了不存在的命令,导致入队失败,整个事务都将无法执行 。 第二个例子: 事务操作入队时,命令和操作的数据类型不匹配 ,入队列正常,但执行 EXEC 命令异常 。 redis> MULTI OK redis> SET msg "other msg" QUEUED redis> SET mystring "I am a string" QUEUED redis> HMSET mystring name "test" QUEUED redis> SET msg "after" QUEUED redis> EXEC 1) OK 2) OK 3) (error) WRONGTYPE Operation against a key holding the wrong kind of value 4) OK redis> GET msg "after" 这个例子里,Redis 在执行 EXEC 命令时,如果出现了错误,Redis 不会终止其它命令的执行,事务也不会因为某个命令执行失败而回滚 。 综上,我对 Redis 事务原子性的理解如下: 命令入队时报错, 会放弃事务执行,保证原子性; 命令入队时正常,执行 EXEC 命令后报错,不保证原子性; 也就是:Redis 事务在特定条件下,才具备一定的原子性 。 2.2 隔离性 数据库的隔离性是指:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。 事务隔离分为不同级别 ,分别是: 未提交读(read uncommitted) 提交读(read committed) 可重复读(repeatable read) 串行化(serializable) 首先,需要明确一点:Redis 并没有事务隔离级别的概念。这里我们讨论 Redis 的隔离性是指:并发场景下,事务之间是否可以做到互不干扰。 我们可以将事务执行可以分为 EXEC 命令执行前和 EXEC 命令执行后两个阶段,分开讨论。 EXEC 命令执行前 在事务原理这一小节,我们发现在事务执行之前 ,Redis key 依然可以被修改。此时,可以使用 WATCH 机制来实现乐观锁的效果。 EXEC 命令执行后 因为 Redis 是单线程执行操作命令, EXEC 命令执行后,Redis 会保证命令队列中的所有命令执行完 。 这样就可以保证事务的隔离性。 2.3 持久性 数据库的持久性是指 :事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。 Redis 的数据是否持久化取决于 Redis 的持久化配置模式 。 没有配置 RDB 或者 AOF ,事务的持久性无法保证; 使用了 RDB模式,在一个事务执行后,下一次的 RDB 快照还未执行前,如果发生了实例宕机,事务的持久性同样无法保证; 使用了 AOF 模式;AOF 模式的三种配置选项 no 、everysec 都会存在数据丢失的情况 。always 可以保证事务的持久性,但因为性能太差,在生产环境一般不推荐使用。 综上,redis 事务的持久性是无法保证的 。 2.4 一致性 一致性的概念一直很让人困惑,在我搜寻的资料里,有两类不同的定义。 维基百科 我们先看下维基百科上一致性的定义: Consistency ensures that a transaction can only bring the database from one valid state to another, maintaining database invariants: any data written to the database must be valid according to all defined rules, including constraints, cascades, triggers, and any combination thereof. This prevents database corruption by an illegal transaction, but does not guarantee that a transaction is correct. Referential integrity guarantees the primary key – foreign key relationship. 在这段文字里,一致性的核心是“约束”,“any data written to the database must be valid according to all defined rules ”。 如何理解约束?这里引用知乎问题 如何理解数据库的内部一致性和外部一致性,蚂蚁金服 OceanBase 研发专家韩富晟回答的一段话: “约束”由数据库的使用者告诉数据库,使用者要求数据一定符合这样或者那样的约束。当数据发生修改时,数据库会检查数据是否还符合约束条件,如果约束条件不再被满足,那么修改操作不会发生。 关系数据库最常见的两类约束是“唯一性约束”和“完整性约束”,表格中定义的主键和唯一键都保证了指定的数据项绝不会出现重复,表格之间定义的参照完整性也保证了同一个属性在不同表格中的一致性。 “ Consistency in ACID ”是如此的好用,以至于已经融化在大部分使用者的血液里了,使用者会在表格设计的时候自觉的加上需要的约束条件,数据库也会严格的执行这个约束条件。 所以事务的一致性和预先定义的约束有关,保证了约束即保证了一致性。 我们细细品一品这句话: This prevents database corruption by an illegal transaction, but does not guarantee that a transaction is correct。 写到这里可能大家还是有点模糊,我们举经典转账的案例。 我们开启一个事务,张三和李四账号上的初始余额都是1000元,并且余额字段没有任何约束。张三给李四转账1200元。张三的余额更新为 -200 , 李四的余额更新为2200。 从应用层面来看,这个事务明显不合法,因为现实场景中,用户余额不可能小于 0 , 但是它完全遵循数据库的约束,所以从数据库层面来看,这个事务依然保证了一致性。 Redis 的事务一致性是指:Redis 事务在执行过程中符合数据库的约束,没有包含非法或者无效的错误数据。 我们分三种异常场景分别讨论: 执行 EXEC 命令前,客户端发送的操作命令错误,事务终止,数据保持一致性; 执行 EXEC 命令后,命令和操作的数据类型不匹配,错误的命令会报错,但事务不会因为错误的命令而终止,而是会继续执行。正确的命令正常执行,错误的命令报错,从这个角度来看,数据也可以保持一致性; 执行事务的过程中,Redis 服务宕机。这里需要考虑服务配置的持久化模式。 无持久化的内存模式:服务重启之后,数据库没有保持数据,因此数据都是保持一致性的; RDB / AOF 模式: 服务重启后,Redis 通过 RDB / AOF 文件恢复数据,数据库会还原到一致的状态。 综上所述,在一致性的核心是约束的语意下,Redis 的事务可以保证一致性。 《设计数据密集型应用》 这本书是分布式系统入门的神书。在事务这一章节有一段关于 ACID 的解释: Atomicity, isolation, and durability are properties of the database,whereas consistency (in the ACID sense) is a property of the application. The application may rely on the database’s atomicity and isolation properties in order to achieve consistency, but it’s not up to the database alone. Thus, the letter C doesn’t really belong in ACID. 原子性,隔离性和持久性是数据库的属性,而一致性(在 ACID 意义上)是应用程序的属性。应用可能依赖数据库的原子性和隔离属性来实现一致性,但这并不仅取决于数据库。因此,字母 C 不属于 ACID 。 很多时候,我们一直在纠结的一致性,其实就是指符合现实世界的一致性,现实世界的一致性才是事务追求的最终目标。 为了实现现实世界的一致性,需要满足如下几点: 保证原子性,持久性和隔离性,如果这些特征都无法保证,那么事务的一致性也无法保证; 数据库本身的约束,比如字符串长度不能超过列的限制或者唯一性约束; 业务层面同样需要进行保障 。 2.5 事务特点 我们通常称 Redis 为内存数据库 , 不同于传统的关系数据库,为了提供了更高的性能,更快的写入速度,在设计和实现层面做了一些平衡,并不能完全支持事务的 ACID。 Redis 的事务具备如下特点: 保证隔离性; 无法保证持久性; 具备了一定的原子性,但不支持回滚; 一致性的概念有分歧,假设在一致性的核心是约束的语意下,Redis 的事务可以保证一致性。 从工程角度来看,假设事务操作中每个步骤需要依赖上一个步骤返回的结果,则需要通过 watch 来实现乐观锁 。 3 Lua 脚本 3.1 简介 Lua 由标准 C 编写而成,代码简洁优美,几乎在所有操作系统和平台上都可以编译,运行。Lua 脚本可以很容易的被 C/C ++ 代码调用,也可以反过来调用 C/C++ 的函数,这使得 Lua 在应用程序中可以被广泛应用。 Lua 脚本在游戏领域大放异彩,大家耳熟能详的《大话西游II》,《魔兽世界》都大量使用 Lua 脚本。Java 后端工程师接触过的 api 网关,比如 Openresty ,Kong 都可以看到 Lua 脚本的身影。 从 Redis 2.6.0 版本开始, Redis内置的 Lua 解释器,可以实现在 Redis 中运行 Lua 脚本。 使用 Lua 脚本的好处 : 减少网络开销。将多个请求通过脚本的形式一次发送,减少网络时延。 原子操作。Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。 复用。客户端发送的脚本会永久存在 Redis 中,其他客户端可以复用这一脚本而不需要使用代码完成相同的逻辑。 Redis Lua 脚本常用命令: 序号 命令及描述 1 EVAL script numkeys key [key ...] arg [arg ...] 执行 Lua 脚本。 2 EVALSHA sha1 numkeys key [key ...] arg [arg ...] 执行 Lua 脚本。 3 SCRIPT EXISTS script [script ...] 查看指定的脚本是否已经被保存在缓存当中。 4 SCRIPT FLUSH 从脚本缓存中移除所有脚本。 5 SCRIPT KILL 杀死当前正在运行的 Lua 脚本。 6 SCRIPT LOAD script 将脚本 script 添加到脚本缓存中,但并不立即执行这个脚本。 3.2 EVAL 命令 命令格式: EVAL script numkeys key [key ...] arg [arg ...] 说明: script是第一个参数,为 Lua 5.1脚本; 第二个参数numkeys指定后续参数有几个 key; key [key ...],是要操作的键,可以指定多个,在 Lua 脚本中通过KEYS[1], KEYS[2]获取; arg [arg ...],参数,在 Lua 脚本中通过ARGV[1], ARGV[2]获取。 简单实例: redis> eval "return ARGV[1]" 0 100 "100" redis> eval "return {ARGV[1],ARGV[2]}" 0 100 101 1) "100" 2) "101" redis> eval "return {KEYS[1],KEYS[2],ARGV[1]}" 2 key1 key2 first second 1) "key1" 2) "key2" 3) "first" 4) "second" 下面演示下 Lua 如何调用 Redis 命令 ,通过redis.call()来执行了 Redis 命令 。 redis> set mystring 'hello world' OK redis> get mystring "hello world" redis> EVAL "return redis.call('GET',KEYS[1])" 1 mystring "hello world" redis> EVAL "return redis.call('GET','mystring')" 0 "hello world" 3.3 EVALSHA 命令 使用 EVAL 命令每次请求都需要传输 Lua 脚本 ,若 Lua 脚本过长,不仅会消耗网络带宽,而且也会对 Redis 的性能造成一定的影响。 思路是先将 Lua 脚本先缓存起来 , 返回给客户端 Lua 脚本的 sha1 摘要。 客户端存储脚本的 sha1 摘要 ,每次请求执行 EVALSHA 命令即可。 EVALSHA 命令基本语法如下: redis> EVALSHA sha1 numkeys key [key ...] arg [arg ...] 实例如下: redis> SCRIPT LOAD "return 'hello world'" "5332031c6b470dc5a0dd9b4bf2030dea6d65de91" redis> EVALSHA 5332031c6b470dc5a0dd9b4bf2030dea6d65de91 0 "hello world" 4 事务 VS Lua 脚本 从定义上来说, Redis 中的脚本本身就是一种事务, 所以任何在事务里可以完成的事, 在脚本里面也能完成。 并且一般来说, 使用脚本要来得更简单,并且速度更快。 因为脚本功能是 Redis 2.6 才引入的, 而事务功能则更早之前就存在了, 所以 Redis 才会同时存在两种处理事务的方法。 不过我们并不打算在短时间内就移除事务功能, 因为事务提供了一种即使不使用脚本, 也可以避免竞争条件的方法, 而且事务本身的实现并不复杂。 -- https://redis.io/ Lua 脚本是另一种形式的事务,他具备一定的原子性,但脚本报错的情况下,事务并不会回滚。Lua 脚本可以保证隔离性,而且可以完美的支持后面的步骤依赖前面步骤的结果。 Lua 脚本模式的身影几乎无处不在,比如分布式锁、延迟队列、抢红包等场景。 不过在编写 Lua 脚本时,要注意如下两点: 为了避免 Redis 阻塞,Lua 脚本业务逻辑不能过于复杂和耗时; 仔细检查和测试 Lua 脚本 ,因为执行 Lua 脚本具备一定的原子性,不支持回滚。

优秀的个人博客,低调大师

一文详解扩散模型:DDPM

作者:京东零售 刘岩 扩散模型讲解 前沿 人工智能生成内容(AI Generated Content,AIGC)近年来成为了非常前沿的一个研究方向,生成模型目前有四个流派,分别是生成对抗网络(Generative Adversarial Models,GAN),变分自编码器(Variance Auto-Encoder,VAE),标准化流模型(Normalization Flow, NF)以及这里要介绍的扩散模型(Diffusion Models,DM)。扩散模型是受到热力学中的一个分支,它的思想来源是非平衡热力学(Non-equilibrium thermodynamics)。扩散模型的算法理论基础是通过变分推断(Variational Inference)训练参数化的马尔可夫链(Markov Chain),它在许多任务上展现了超过GAN等其它生成模型的效果,例如最近非常火热的OpenAI的DALL-E 2,Stability.ai的Stable Diffusion等。这些效果惊艳的模型扩散模型的理论基础便是我们这里要介绍的提出扩散模型的文章[1]和非常重要的DDPM[2],扩散模型的实现并不复杂,但其背后的数学原理却非常丰富。在这里我会介绍这些重要的数学原理,但省去了这些公式的推导计算,如果你对这些推导感兴趣,可以学习参考文献[4,5,11]的相关内容。我在这里主要以一个相对简单的角度来讲解扩散模型,帮助你快速入门这个非常重要的生成算法。 1. 背景知识: 生成模型 目前生成模型主要有图1所示的四类。其中GAN的原理是通过判别器和生成器的互相博弈来让生成器生成足以以假乱真的图像。VAE的原理是通过一个编码器将输入图像编码成特征向量,它用来学习高斯分布的均值和方差,而解码器则可以将特征向量转化为生成图像,它侧重于学习生成能力。流模型是从一个简单的分布开始,通过一系列可逆的转换函数将分布转化成目标分布。扩散模型先通过正向过程将噪声逐渐加入到数据中,然后通过反向过程预测每一步加入的噪声,通过将噪声去掉的方式逐渐还原得到无噪声的图像,扩散模型本质上是一个马尔可夫架构,只是其中训练过程用到了深度学习的BP,但它更属于数学层面的创新。这也就是为什么很多计算机的同学看扩散模型相关的论文会如此费力。 图1:生成模型的四种类型 [4] 扩散模型中最重要的思想根基是马尔可夫链,它的一个关键性质是平稳性。即如果一个概率随时间变化,那么再马尔可夫链的作用下,它会趋向于某种平稳分布,时间越长,分布越平稳。如图2所示,当你向一滴水中滴入一滴颜料时,无论你滴在什么位置,只要时间足够长,最终颜料都会均匀的分布在水溶液中。这也就是扩散模型的前向过程。 图2:颜料分子在水溶液中的扩散过程 如果我们能够在扩散的过程颜料分子的位置、移动速度、方向等移动属性。那么也可以根据正向过程的保存的移动属性从一杯被溶解了颜料的水中反推颜料的滴入位置。这边是扩散模型的反向过程。记录移动属性的快照便是我们要训练的模型。 2. 扩散模型 在这一部分我们将集中介绍扩散模型的数学原理以及推导的几个重要性质,因为推导过程涉及大量的数学知识但是对理解扩散模型本身思想并无太大帮助,所以这里我会省去推导的过程而直接给出结论。但是我也会给出推导过程的出处,对其中的推导过程比较感兴趣的请自行查看。 2.1 计算原理 扩散模型简单的讲就是通过神经网络学习从纯噪声数据逐渐对数据进行去噪的过程,它包含两个步骤,如图3: 图3:DDPM的前向加噪和后向去噪过程 2.1.1 前向过程 2.1.2 后向过程 2.1.3 目标函数 那么问题来了,我们究竟使用什么样的优化目标才能比较好的预测高斯噪声的分布呢?一个比较复杂的方式是使用变分自编码器的最大化证据下界(Evidence Lower Bound, ELBO)的思想来推导,如式(6),推导详细过程见论文[11]的式(47)到式(58),这里主要用到了贝叶斯定理和琴生不等式。 \(\begin{aligned} \mathcal L & = - \log p(\boldsymbol x) \\ & = - \log \int \frac{p_\theta(\boldsymbol x_{0:T})q(\boldsymbol x_{1:T} | \boldsymbol x_0)}{q(\boldsymbol x_{1:T} | \boldsymbol x_0)} d \boldsymbol x_{1:T} \\ & \leq - \mathbb E_{q(\boldsymbol x_{1:T} | \boldsymbol x_0)} \left[ \frac{p_\theta(\boldsymbol x_{0:T})}{q(\boldsymbol x_{1:T} | \boldsymbol x_0)}\right] \\ & = - \underbrace{\mathbb{E}_{q\left(\boldsymbol{x}_1 \mid \boldsymbol{x}_0\right)}\left[\log p_{\boldsymbol{\theta}}\left(\boldsymbol{x}_0 \mid \boldsymbol{x}_1\right)\right]}_{\text {重构项}} + \underbrace{D_{\mathrm{KL}}\left(q\left(\boldsymbol{x}_T \mid \boldsymbol{x}_0\right) \| p\left(\boldsymbol{x}_T\right)\right)}_{\text {先验匹配项}} + \sum_{t=2}^T \underbrace{\mathbb{E}_{q\left(\boldsymbol{x}_t \mid \boldsymbol{x}_0\right)}\left[D_{\mathrm{KL}}\left(q\left(\boldsymbol{x}_{t-1} \mid \boldsymbol{x}_t, \boldsymbol{x}_0\right) \| p_{\boldsymbol{\theta}}\left(\boldsymbol{x}_{t-1} \mid \boldsymbol{x}_t\right)\right)\right]}_{\text {去噪匹配项}} \end{aligned} \tag6\) 式(6)的推导细节并不重要,我们需要重点关注的是它的最终等式的三个组成部分,下面我们分别介绍它们: 图4:扩散模型的去噪匹配项在每一步都要拟合噪音的真实后验分布和估计分布 真实后验分布可以使用贝叶斯定理进行推导,最终结果如式(8),推导过程见论文[11]的式(71)到式(84)。 \(\begin{aligned} q(\boldsymbol x_{t-1} | \boldsymbol x_t, \boldsymbol x_0) & = \frac{q(\boldsymbol x_{t} | \boldsymbol x_{t-1}, \boldsymbol x_0) q(\boldsymbol x_{t-1} | \boldsymbol x_0)}{q(\boldsymbol x_{t} | \boldsymbol x_0)} \ & \propto \mathcal N \left( \boldsymbol x_{t-1}; \frac{\sqrt{\alpha_t} (1 - \bar{\alpha}{t-1}) \boldsymbol x_t + \sqrt{\bar{\alpha}{t-1}}(1 - \alpha_t) \boldsymbol x_0}{ 1- \bar{\alpha}t}, \frac{(1 - \alpha_t)(1 - \bar{\alpha}{t-1})}{1 - \bar{\alpha}t} \mathbf I \right) \ & = \mathcal N(\boldsymbol x{t-1}; \mu_q(\boldsymbol x_t, \boldsymbol x_0), \Sigma_q(t)) \end{aligned} \tag8 \) \(p{\boldsymbol{\theta}}\left(\boldsymbol{x}{t-1} \mid \boldsymbol{x}t\right) = \mathcal N(\boldsymbol x{t-1}; \mu\theta(\boldsymbol x_t, t), \Sigma_q(t)) \tag9\) \( \begin{aligned} & D_\text{KL}(\mathcal N(\boldsymbol x; \boldsymbol \mu_x, \boldsymbol \Sigma_x), \mathcal N(\boldsymbol y; \boldsymbol \mu_y, \boldsymbol \Sigma_y) \ = & \frac{1}{2}\left[ \log \frac{|\boldsymbol \Sigma_x|}{|\boldsymbol \Sigma_y|} - d + \text{tr}(\boldsymbol \Sigma_y^{-1} \boldsymbol \Sigma_x) + (\boldsymbol \mu_y - \boldsymbol \mu_x)^\intercal \boldsymbol \sigma_y^{-1}(\boldsymbol \mu_y - \boldsymbol \mu_x)\right]) \end{aligned} \tag{10} \) \( \begin{aligned} \mathop{\arg\min}\theta D\text{KL}(q(\boldsymbol x_{t-1} | \boldsymbol x_t, \boldsymbol x_0) || p_\theta(\boldsymbol x_{t-1} | \boldsymbol x_t)) = \mathop{\arg\min}\theta \frac{1}{2\sigma_q^2(t)} \left[|\boldsymbol \mu\theta (\boldsymbol x_t, \boldsymbol x_0) - \boldsymbol \mu_q(\boldsymbol x_t, t) |^2_2\right] \ \end{aligned} \tag{11} \) 2.1.4 模型训练 虽然上面我们介绍了很多内容,并给出了大量公式,但得益于推导出的几个重要性质,扩散模型的训练并不复杂,它的训练伪代码见算法1。 2.1.5 样本生成 2.2 算法实现 2.2.1模型结构 DDPM在预测施加的噪声时,它的输入是施加噪声之后的图像,预测内容是和输入图像相同尺寸的噪声,所以它可以看做一个Img2Img的任务。DDPM选择了U-Net[9]作为噪声预测的模型结构。U-Net是一个U形的网络结构,它由编码器,解码器以及编码器和解码器之间的跨层连接(残差连接)组成。其中编码器将图像降采样成一个特征,解码器将这个特征上采样为目标噪声,跨层连接用于拼接编码器和解码器之间的特征。 图5:U-Net的网络结构 下面我们介绍DDPM的模型结构的重要组件。首先在U-Net的卷积部分,DDPM使用了宽残差网络(Wide Residual Network,WRN)[12]作为核心结构,WRN是一个比标准残差网络层数更少,但是通道数更多的网络结构。也有作者复现发现ConvNeXT作为基础结构会取得非常显著的效果提升[13,14]。这里我们可以根据训练资源灵活的调整卷积结构以及具体的层数等超参。因为我们在扩散过程的整个流程中都共享同一套参数,为了区分不同的时间片,作者借鉴了Transformer [15]的位置编码的思想,采用了正弦位置嵌入对时间$t$进行了编码,这使得模型在预测噪声时知道它预测的是批次中分别是哪个时间片添加的噪声。在卷积层之间,DDPM添加了一个注意力层。这里我们可以使用Transformer中提出的自注意力机制或是多头自注意力机制。[13]则提出了一个线性注意力机制的模块,它的特点是消耗的时间以及占用的内存和序列长度是线性相关的,对比传统注意力机制的平方相关要高效很多。在进行归一化时,DDPM选择了组归一化(Group Normalization,GN)[16]。最后,对于U-Net中的降采样和上采样操作,DDPM分别选择了步长为2的卷积以及反卷积。 确定了这些组件,我们便可以搭建用于DDPM的U-Net的模型了。从第2.1节的介绍我们知道,模型的输入为形状为(batch_size, num_channels, height, width)的噪声图像和形状为(batch_size,1)的噪声水平,返回的是形状为(batch_size, num_channels, height, width)的预测噪声,我们搭建的用于噪声预测的模型结构如下: 首先在噪声图像\( \boldsymbol x_0\)上应用卷积层,并为噪声水平$t$计算时间嵌入; 接下来是降采样阶段。采用的模型结构依次是两个卷积(WRNS或是ConvNeXT)+GN+Attention+降采样层; 在网络的最中间,依次是卷积层+Attention+卷积层; 接下来是上采样阶段。它首先会使用Short-cut拼接来自降采样中同样尺寸的卷积,再之后是两个卷积+GN+Attention+上采样层。 最后是使用WRNS或是ConvNeXT作为输出层的卷积。 U-Net类的forword函数如下面代码片段所示,完整的实现代码参照[3]。 def forward(self, x, time): x = self.init_conv(x) t = self.time_mlp(time) if exists(self.time_mlp) else None h = [] # downsample for block1, block2, attn, downsample in self.downs: x = block1(x, t) x = block2(x, t) x = attn(x) h.append(x) x = downsample(x) # bottleneck x = self.mid_block1(x, t) x = self.mid_attn(x) x = self.mid_block2(x, t) # upsample for block1, block2, attn, upsample in self.ups: x = torch.cat((x, h.pop()), dim=1) x = block1(x, t) x = block2(x, t) x = attn(x) x = upsample(x) return self.final_conv(x) 2.2.2 前向加噪 图6:一张图依次经过0次,50次,100次,150次以及199次加噪后的效果图 根据式(14)我们知道,扩散模型的损失函数计算的是两张图像的相似性,因此我们可以选择使用回归算法的所有损失函数,以MSE为例,前向过程的核心代码如下面代码片段。 def p_losses(denoise_model, x_start, t, noise=None, loss_type="l1"): # 1. 根据时刻t计算随机噪声分布,并对图像x_start进行加噪 x_noisy = q_sample(x_start=x_start, t=t, noise=noise) # 2. 根据噪声图像以及时刻t,预测添加的噪声 predicted_noise = denoise_model(x_noisy, t) # 3. 对比添加的噪声和预测的噪声的相似性 loss = F.mse_loss(noise, predicted_noise) return loss 2.2.3 样本生成 根据2.1.5节介绍的样本生成流程,它的核心代码片段所示,关于这段代码的讲解我通过注释添加到了代码片段中。 @torch.no_grad() def p_sample(model, x, t, t_index): betas_t = extract(betas, t, x.shape) sqrt_one_minus_alphas_cumprod_t = extract(sqrt_one_minus_alphas_cumprod, t, x.shape) sqrt_recip_alphas_t = extract(sqrt_recip_alphas, t, x.shape) # 使用式(13)计算模型的均值 model_mean = sqrt_recip_alphas_t * (x - betas_t * model(x, t) / sqrt_one_minus_alphas_cumprod_t) if t_index == 0: return model_mean else: # 获取保存的方差 posterior_variance_t = extract(posterior_variance, t, x.shape) noise = torch.randn_like(x) # 算法2的第4行 return model_mean + torch.sqrt(posterior_variance_t) * noise # 算法2的流程,但是我们保存了所有中间样本 @torch.no_grad() def p_sample_loop(model, shape): device = next(model.parameters()).device b = shape[0] # start from pure noise (for each example in the batch) img = torch.randn(shape, device=device) imgs = [] for i in tqdm(reversed(range(0, timesteps)), desc='sampling loop time step', total=timesteps): img = p_sample(model, img, torch.full((b,), i, device=device, dtype=torch.long), i) imgs.append(img.cpu().numpy()) return imgs 最后我们看下在人脸图像数据集下训练的模型,一批随机噪声经过逐渐去噪变成人脸图像的示例。 图7:扩散模型由随机噪声通过去噪逐渐生成人脸图像 3. 总结 这里我们以DDPM为例介绍了另一个派系的生成算法:扩散模型。扩散模型是一个基于马尔可夫链的数学模型,它通过预测每个时间片添加的噪声来进行模型的训练。作为近日来引发热烈讨论的ControlNet, Stable Diffusion等模型的底层算法,我们十分有必要对其有所了解。DDPM的实现并不复杂,这得益于大量数学界大佬通过大量的数学推导将整个扩散过程和反向去噪过程进行了精彩的化简,这才有了DDPM的大道至简的实现。DDPM作为一个扩散模型的基石算法,它有着很多早期算法的共同问题: 采样速度慢:DDPM的去噪是从时刻$T$到时刻$1$的一个完整的马尔可夫链的计算,尤其是DDPM还需要一个比较大的$T$才能保证比较好的效果,这就导致了DDPM的采样过程注定是非常慢的; 生成效果差:DDPM的效果并不能说是非常好,尤其是对于高分辨率图像的生成。这一方面是因为它的计算速度限制了它扩展到更大的模型;另一方面它的设计还有一些问题,例如逐像素的计算损失并使用相同权值而忽略图像中的主体并不是非常好的策略。 内容不可控:我们可以看出,DDPM生成的内容完全还是取决于它的训练集。它并没有引入一些先验条件,因此并不能通过控制图像中的细节来生成我们制定的内容。 我们现在已经知道,DDPM的这些问题已大幅得到改善,现在基于扩散模型生成的图像已经达到甚至超过人类多数的画师的效果,我也会在之后逐渐给出这些优化方案的讲解。 Reference [1] Sohl-Dickstein, Jascha, et al. "Deep unsupervised learning using nonequilibrium thermodynamics." International Conference on Machine Learning. PMLR, 2015. [2] Ho, Jonathan, Ajay Jain, and Pieter Abbeel. "Denoising diffusion probabilistic models." Advances in Neural Information Processing Systems 33 (2020): 6840-6851. [3] https://huggingface.co/blog/annotated-diffusion [4] https://lilianweng.github.io/posts/2021-07-11-diffusion-models/#simplification [5] https://openai.com/blog/generative-models/ [6] Nichol, Alexander Quinn, and Prafulla Dhariwal. "Improved denoising diffusion probabilistic models." International Conference on Machine Learning. PMLR, 2021. [7] Kingma, Diederik P., and Max Welling. "Auto-encoding variational bayes." arXiv preprint arXiv:1312.6114 (2013). [8] Hinton, Geoffrey E., and Ruslan R. Salakhutdinov. "Reducing the dimensionality of data with neural networks." science 313.5786 (2006): 504-507. [9] Ronneberger O, Fischer P, Brox T. U-net: Convolutional networks for biomedical image segmentation[C]//International Conference on Medical image computing and computer-assisted intervention. Springer, Cham, 2015: 234-241. [10] Long, Jonathan, Evan Shelhamer, and Trevor Darrell. "Fully convolutional networks for semantic segmentation." Proceedings of the IEEE conference on computer vision and pattern recognition. 2015. [11] Luo, Calvin. "Understanding diffusion models: A unified perspective." arXiv preprint arXiv:2208.11970 (2022). [12] Zagoruyko, Sergey, and Nikos Komodakis. "Wide residual networks." arXiv preprint arXiv:1605.07146 (2016). [13] https://github.com/lucidrains/denoising-diffusion-pytorch [14] Liu, Zhuang, et al. "A convnet for the 2020s." Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition. 2022. [15] Vaswani, Ashish, et al. "Attention is all you need." Advances in neural information processing systems 30 (2017). [16] Wu, Yuxin, and Kaiming He. "Group normalization." Proceedings of the European conference on computer vision (ECCV). 2018.

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册