-
Recent Comments
-
Boinc
Category Archives: Uncategorized
PyTorch模型训练经验教训之一
本文为过去近一个月,我跑PyTorch模型训练所获得的经验和教训。 本文所述“优化”是指提高训练速度、降低训练时间 ,效果是指模型的评价指标结果。
Posted in Uncategorized
Leave a comment
PaddleNLP安装
PaddleNLP可能是安装体验最差的ML程序了。安装和安装过程中,你可能会遭遇一系列错误。下面挨个介绍。 说明,我的操作系统是Fedora 38和Debian 12。当前PaddleNLP最新版本为:v2.6.1,Paddle最新版本为v2.5.0。下面只包含我当前遇到过的问题。 一、libssl.so.1.1: cannot open shared object file: No such file or directory。 系统没有OpenSSL 1.1。Fedora系统,直接安装openssl1.1即可。Debian系统,需要下载OpenSSL 1.1源码,编译安装。 二、MemoryError: (ResourceExhausted) Fail to alloc memory of XXX size。 不是电脑内存不足。切换到PaddleNLP 2.4.0和Paddle 2.4.2即可。 三、实例代码执行结果与预期不符。比如对“2月8日上午北京冬奥会自由式滑雪女子大跳台决赛中中国选手谷爱凌以188.25分获得金牌!”执行information_extraction信息抽取,结果为空。 切换到PaddleNLP 2.4.0和Paddle 2.4.2即可。 四、PaddleNLP和Paddle所依赖的protobuf版本冲突。 PaddleNLP 2.4.0和Paddle 2.4.2依赖的protobuf版本不冲突。或者可以查下PaddleNLP和Paddle各个tag下requirements.txt要求的protobuf版本。 五、pip提示:ERROR: Could … Continue reading
一种从客观数据到代码实现的编程思想
这套编程思想,从我日常工作中总结得来,用于解决一些复杂业务问题。内容偏抽象,表达能力有限,不喜请绕道。 一、调研客观数据,放弃主观思考。 以往解决问题,往往基于认知和思考。认知是有限的,主观思考易产生偏见。 解决问题,就从问题本身出发,尽可能搜集问题的表现数据,找出数据特征和规律。最后,程序要的做,就是把数据的特征和规律描述出来。 二、程序是数据的处理过程。 所有程序,最顶层的抽象,都是数据的“输入 – 处理 – 输出”。如下图所示: 实现中,中间的“处理”过程,是程序的主要复杂点。“计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决”。对于复杂的处理,一个中间层不够,那就多个,于是可以得到: 在上面的模型中,每层都封装了数据。一层的数据,转换为下一层数据的过程,就是逻辑。 下面是一个虚构的商品请求程序模型: 这个商品请求程序模型,存在五层数据。第一层的数据是“商品请求数据”,通过逻辑“查询商品”,转换为第二层的数据“商品基础数据”…… 程序也可以描述为:输入数据,经过多层转换处理,最终得到输出数据的过程。 三、数据模型和关联。 数据(模型/结构)由属性/字段组成,不带操作/逻辑(逻辑被视为数据的转换过程)。每层有一到多个/块数据。单层内的数据之前可能存在关联,这种关联在代码上表示为数据间的组合。 下面是一组虚拟的用户和钱包数据模型: 上面的数据模型,如果用代码来实现,可能是下面这样的: 四、完整的可实现的程序模型。 一个程序模型示例(省略了数据模型细节): 在上面的示例模型中:存在多道程序逻辑(数据转换逻辑),模块间的边界清晰,部分数据和逻辑被复用,部分数据被复制而不是转换到下一层(实际代码中可能什么也没干)。 下面是一个虚拟的下单请求程序示例(只有一道程序逻辑): 最后要做的,就是用代码,把这个程序模型描述出来。 下面是一个真实案例的模型(省略了大量细节): 上面真实案例模型,是一个邮件发送程序,包含一个邮件发送的通用框架,这个框架支持不同业务(图中的1、2、3)的邮件。
新的Go语言缓存中间件:restcache
我开发的第一套缓存中间件是cachex。cachex支持自定义存储后端,实现了哨兵机制,自带lru存储,支持系统故障时使用过期缓存数据。 cachex的主要问题是不支持批量处理。这也导致在上家公司,cachex未能上阵。我在上家三年时间一直不能开发出支持批量处理的缓存中间件,我也写了三年业务耦合的缓存处理代码。 这次的restcache是fastrest组件库的一个组件。在写restcache之前,我创建了gox项目,实现了优化版的lru,和一套完整的支持批量处理的哨兵机制,然后再实现restcache就简单多了。解决一个复杂问题的一个有效方法,就是模块拆分。 一套完整的缓存,应该包括三个模块:存储、查询、中间件。三个名字是我取的,不准确。 cachex和restcache实现的就是缓存的中间件,并定义了存储和查询的接口。restcache的用户需要自己实现存储和查询。restcache已经附带了一套lrucache存储,可以直接用。但因为用户的redis库版本会不同,restcache没有实现redis存储。 典型的缓存流程如下: 应该绝大多数缓存都是这个流程。这个流程的主要麻烦有两个: 解决方案为: restcache刚刚开发出来,虽然我写了很多单元测试,但经过项目的考验才能成为一个成熟稳定的组件。
reloader:Go服务热升级支持
以前我在公司写过一个简单的Go热升级支持程序。但是工作时间写的,领导不准开源。后我想另写一个开源版本,但一直没想到解决多端口的问题。后来就慢慢忘却了。 直到最近一次偶然的机会,这个程序被人重新提起。又遇上待业期,正需要写点东西打发时间。突然灵感来了,想到了多端口的解决方案。 于是有了reloader。 思路基本是之前版本的扩展版。一个master进程,多个worker进程,一个worker进程处理一个侦听器。这样可以实现多端口的动态侦听和关闭。我又做了些进程管理的优化,推送到github。 无法否认,我的解决方案存在很多问题,因为偷偷起了多进程。比如程序在所有侦听器和连接都关闭后需要上层逻辑结束,这对于单进程无碍,对于reloader却是可能多了N个垃圾worker进程。这些问题,得等待找到更好的解决方案。
遭遇MongoDB勒索
最近测试环境表现得不正常,MongoDB的数据展现不出来。 查了日志,发现有这么几条: 2017-02-08T23:28:32.384+0000 I COMMAND [conn773] dropDatabase PLEASE_READ_ME starting 2017-02-08T23:28:32.388+0000 I COMMAND [conn773] dropDatabase PLEASE_READ_ME finished 2017-02-08T23:28:34.581+0000 I COMMAND [conn773] dropDatabase iwesee starting 2017-02-08T23:28:34.601+0000 I COMMAND [conn773] dropDatabase iwesee finished 我的代码中不可能有dropDatabase的操作,也没这么手动操作过。 既然PLEASE_READ_ME,那我去就读。 在数据库中发现了PLEASE_READ_ME库,库下有PLEASE_READ_ME集合,集合中有这么个文档: { “_id” : ObjectId(“58a06f3a6d3d693e86ae59da”), “info” : “Don’t … Continue reading
实现golang程序热升级
更新:已实现一个开源版本reloader。 简单定义热升级就是:让运行中的服务改为执行新的程序代码逻辑,且不中断服务。 目前现有的开源热升级程序主要有endless,和beego的grace组件。 经过测试,grace在热升级时,会掐掉当前处理中的连接,自然无法接受。endless的问题在于每次热升级都是创建子进程后,原进程就退出了,这点显然不符合守护进程的要求。 于是自己实现个新的。逻辑参考了endless。其实endless和grace也是相互“借鉴”,相互copy了很多代码。我在“借鉴”时,力求逻辑尽可能简单,自己编写全部代码,并后续完善一些关键细节。 首先要解决多个进程监听同一地址+端口。我首先想到的是nginx、node.js的端口复用方案。但golang尚不支持端口复用。好在golang支持文件描述符和listener的互转,和文件描述符的继承。方案为主进程创建listener,取得文件描述符,子进程再继承listener文件描述符。 新进程应该执行新程序的逻辑。要实现这点,就不能用fork,不然新进程还是执行旧的程序逻辑。 支持守护进程。这点是我重新实现热升级的关键。endless和grace的逻辑是,创建子进程,然后原进程退出。我的方案是一个master进程,一个worker进程。每次只升级worker进程。 master进程与worker进程在外部看起来应该是一体的。这里沿袭了erlang任其崩溃的思想,一个退出,另一个跟随。如果worker进程退出码非0,master进程以相同的退出码结束。 还有很重要的一点,退出worker进程,首先关闭listener,一并关闭listener关联的文件描述符,再等待所有连接关闭。 支持自动升级。这个不是很必要,以后再说。 相比endless,我的实现的另一个优势是:只提供listener接口,而不是ListenAndServe函数。也就是该实现不仅支持http/https,同时支持其它基于tcp的应用层协议,比如grpc。
自定义tornado日志格式
第一次玩tornado。版本4.x。为了解决日志格式的问题,google了很多,没一个有效的。 tornado日志格式分两块,一块是logging的格式,一块是tornado请求消息格式。 tornado默认的访问日志输出是这样的: WARNING:tornado.access:404 GET / (127.0.0.1) 167.93ms 其中,WARNING:tornado.access:404为logger日志内容,GET / (127.0.0.1) 167.93ms为访问消息。 根据enable_pretty_logging函数,可知tornado默认采用logging的root logger,并且,如果logger没有配置handler,则为其添加一个默认的StreamHandler。 因而,应该为logging root logger预创建Handler,并指定日志格式。 依据tornado文档,输出访问日志的为Application.log_request,如果要自定义访问日志输出,可以继承Application并重写log_request,或者自定义log_request函数,并设置为Application.settings的log_function属性。 当然选择后者。log_function函数的写法,参照原版Application.log_request就行。 最后贴上我的代码供Google到此的朋友参考: import logging import tornado.log # 日志格式 logging.getLogger().setLevel(logging.INFO) formatter = logging.Formatter(fmt=”%(levelname).4s %(asctime)s %(name)s %(message)s”, datefmt=”%Y-%m-%d %H:%M:%S”) handler = logging.StreamHandler() handler.setFormatter(formatter) logging.getLogger().addHandler(handler) … Continue reading