作者归档:vicalloy

关于kkndme谈房产

不知道为啥,kkndme的帖子忽然出现在GitHub的热榜上,而且被冠以神贴的称号。简单的看了一下缩水版的帖子。帖子有些意思,但要说是神帖似乎有些过了。

总结

别指望房子降价,房子是中国割韭菜的主要工具,没有找到替代手段前国家不会放弃的(暂时也找不到)。别老想着买房投资。房子是给国家赚钱的,你们这些二手房就别来添乱了。

看法

kkndme文中公知味很足,文中的内容要辩证的看。11年后房价确实涨过一波,不过并不是一直在涨。10年过去了,国内外形势已经有很大的不同,房子还能不能涨还真不一定(也别指望跌)。房价重要的是要维持在一个“合理的范围内”。多少钱合理,主要看能掏钱买房的“刚需”有多少。别被打着kkndme旗号的公众号收智商税。

在线使用的图片风格迁移工具

近期研究 ONNX Runtime Web 做的一个小东西。很多代码都“借鉴”于其他开源项目,解决了图片变形等问题。

使用深度学习模型做的图片风格迁移。使用 React 和 ONNX Runtime Web 开发,推理后端用的 WebAssembly ( CPU )。根据我的测试,用 WebGL 要慢不少,而且内存占用有些夸张。

在线访问: https://vicalloy.github.io/image-transformer/

项目地址: https://github.com/vicalloy/image-transformer

Note:

  1. 所有推理工作在浏览器完成,不需要消耗服务器资源。
  2. 推理的时候需要选择图片的输出大小。不同大小的输出用的模型不同。
    • 越大的输出尺寸需要耗费的算力越多(时间越长),测试的时候可以先用小尺寸看效果。
    • fast neural style这个模型也可以支持输出任意图片大小,不过动态参数模型太大,复杂度也高,不适合 Web 使用。

新疆之行

本计划去年去新疆,不想因为疫情的关系错过了,今年算是把去年的计划做了个补完。由于错误的低估了新疆入冬的速度,未能走成独库公路。这是一次不完美,甚至是失败的旅行,不过依旧留下了一些美好。

赛里木湖

9月底的新疆早已入秋,草早已黄了,湖边的景致早已不如盛夏。湖很完美,比青海湖要漂亮。风很大,湖很蓝。浪花拍打着湖岸,然人觉赛里木湖更像是大海。

如果有机会再来赛湖,会选择在湖边住一晚。在湖边发发呆还是很惬意的。

夏塔

草地以及不远处的雪山,是我喜欢的画面。只是现在已经过了夏塔最漂亮的季节。出发前就一直很纠结要不要去夏塔。

由于规划不周,第一天车开到晚上11点才找到住处。第二天选择了去距离比较近的那拉提草原。

那拉提

也许是季节的原由,那拉提并没有给我很惊艳的感觉。独库路上风景要比那拉提漂亮。

巴音布鲁克

一望无际的大草原。如果夏季过来,应当会是遍地的野花吧。

独库公路

新疆之行最大的期待就是独库公路,不想独库公路瞬间入冬。独库封路,在山下等了一天,第二天依旧没有任何解封的迹象,只能绕道赛湖回乌鲁木齐。

第一天从上午11点一直等到下午5点,不慎将汽车电瓶耗尽,无法启动。非常感谢检查站的小哥帮忙找了个车接电,不然荒郊野外的还不知道修车厂来不来。

虽然只走了那拉提到巴音布鲁克这一小段独库公路,不过已经可以一窥独库公路的美了。

家里的小可爱

或许这次旅行并不能留下太多的记忆,但希望能成为他成长过程中美好经历的一部分。

2021年Python工具链

1. Python虚拟环境:Poetry

一个类似Pipenv的Python虚拟环境和依赖管理的工具,据称改善了一些Pipenv的问题。对我而言,主要看重了Poetry可以对Python库打包的功能。毕竟对我而言书写 setup.py 并不是一件很让人愉快的事情。

2. 代码静态扫描:Flake8

Flake8使用起来非常简单,不用这么配置就可以直接使用,之后检查过程中遇到自己不需要的规则,加个例外就好。

Flake8支持插件,通过添加插件还可以让Flake8功能变的更为强大。

[2023-02 update]: 我的代码检查工具切换成了 ruff 。速度要快很多,且同样易用。

3. 代码自动格式化:Black

写代码时,我个人会尽量遵守 PEP8 ,但难保团队中有些人代码写的有些随意。为保证编码风格的统一,在代码提交前统一由Black对代码镜像格式化。自动格式化之后的代码可能会少了那么一点个性,但为了统一还是值得的。

4. Import规则检查&格式化工具:isort

Black不会对Python 的 import 语句进行排序和分段,这个工作就交给isort来做了。

5. 类型检查:Mypy

长久以来Python作为脚本语言,程序里没有类型信息,很多本可在编译阶段发现的问题被保留到运行时。Python在3.5之后开始支持 Type Hint 了。利用Mypy可以利用这些类型信息对程序进行校验。

6. 单元测试:pytest

相比 unittest ,pytest使用上更为方便。更为重要的是pytest兼容 unittest,似乎没有什么理由来拒绝pytest。 

7. 测试覆盖率:Coverage.py

代码覆盖率测试工具好像也没有第二个选择。

8. pre-commit

git commit 时调用flake8进行代码检查,调用black对代码进行格式化等操作。利用pre-commit从源头上杜绝有人把不合格的代码提交到代码库。

9. Docker、Gitlab-CI、GitHub Action、Travis CI

CI服务可根据自己的实际情况进行选择

将OpenVINO预训练模型转为为ONNX,并使用TVM进行优化

OpenVINO是Intel推出的一款深度学习工具套件。OpenVINO带来大量的预训练模型,使用这些预训练模型可以快速的开发出自己的AI应用。

不过既然是Intel出的东西,自然少不了和Intel平台深度绑定。OpenVINO主要针对Intel的CPU进行优化。虽然也可以支持GPU,但支持的是Intel家的GPU。Intel家的GPU,应当不用报太多期待了。

为了支持更丰富的硬件类型,可以将OpenVINO自带的预训练模型 转为ONNX格式,然后在做其他处理。

OpenVINO模型导出为ONNX

OpenVINO优化后的预训练模型无法直接转换为ONNX。不过好在Intel有提供模型的训练和导出工具,利用OpenVINO的训练工具导出ONNX

OpenVINO用于训练和导出的库为: https://github.com/openvinotoolkit/training_extensions

具体的操作方式参见项目的具体说明文档。

对照人脸检测的文档,导出人脸检测对应ONNX模型: https://github.com/openvinotoolkit/training_extensions/tree/develop/models/object_detection/model_templates/face-detection

注:导出目录里有 export/export/alt_ssd_export/ 两种模型。其中 export/alt_ssd_export/ 包含了OpenVINO特有的实现,在转换为其他推理引擎模型时会失败,因此后续工作使用 export/ 中的模型。

使用TVM对ONNX模型进行优化

针对TVM的VM进行优化

对于存在动态shape的模型,TVM无法进行编译。很不幸的是OpenVINO中物体检测相关的模型都存在动态shape。在TVM无法编译的情况下,可使用TVM的VM进行执行。

  • 注:
    • 关于VM的相关内容请阅读: https://tvm.apache.org/docs/dev/virtual_machine.html
    • TVM的文档比较欠缺(特别是VM相关的内容)。不过好在项目还在快速迭代过程中,提交的issue很快就可以得到回复。
    • 根据测试,使用VM模式,在CPU上TVM的速度甚至比用 ONNXRuntime 还要慢不少。不知道是否是跑在虚拟机上的关系。
import onnx
import time
import tvm
import numpy as np
import tvm.relay as relay
target = 'llvm -mcpu=skylake'
model_path = 'face-detection-0200.onnx'
onnx_model = onnx.load(model_path)
shape = [1,3,256,256]
input_name = "image"
shape_dict = {
        input_name: shape,
        }
mod, params = relay.frontend.from_onnx(onnx_model, shape_dict)
print(relay.transform.DynamicToStatic()(mod))
with tvm.transform.PassContext(opt_level=3):
    executable = relay.vm.compile(mod, target="llvm", target_host=None, params=params)
code, lib = executable.save()
with open("code.ro", "wb") as fo:
    fo.write(code)
lib.export_library("lib.so")

针对TVM进行编译和优化

如果你的模型可以正常编译,那就没必要采用VM模式了。直接编译理论上优化效果要好很多。这里采用的是TVM范例中给出的图片分类模型。

一个完整的模型优化和执行可以参考官方文档:Compiling and Optimizing a Model with the Python AutoScheduler

import onnx
import time
import tvm
import numpy as np
import tvm.relay as relay
target = 'llvm'
model_name = 'mobilenetv2'
model_path = f'{model_name}.onnx'
onnx_model = onnx.load(model_path)
mod, params = relay.frontend.from_onnx(onnx_model)
with relay.build_config(opt_level=3):
    graph, lib, params = relay.build(mod, target, params=params)
path_lib = f"./{model_name}.so"
lib.export_library(path_lib)
fo=open(f"./{model_name}.json","w")
fo.write(graph)
fo.close()
fo=open("./{model_name}.params","wb")
fo.write(relay.save_param_dict(params))
fo.close()

VM模式下加载和运行优化好的模型

加载前面导出的模型,并执行。


import onnx
import time
import tvm
import numpy as np
import tvm.relay as relay
def vmobj_to_array(o, dtype=np.float32):
    if isinstance(o, tvm.nd.NDArray):
        return [o.asnumpy()]
    elif isinstance(o, tvm.runtime.container.ADT):
        result = []
        for f in o:
            result.extend(vmobj_to_array(f, dtype))
        return result
    else:
        raise RuntimeError("Unknown object type: %s" % type(o))
shape = [1, 3, 224, 224]
model_path = 'face-detection-0200'
loaded_lib = tvm.runtime.load_module(f"{model_path}.tvm.so")
loaded_code = bytearray(open(f"{model_path}.tvm.code", "rb").read())
exe = tvm.runtime.vm.Executable.load_exec(loaded_code, loaded_lib)
ctx = tvm.cpu()
vm = tvm.runtime.vm.VirtualMachine(exe, ctx)
data = np.random.uniform(size=shape).astype("float32")
out = vm.run(data)
out = vmobj_to_array(out)
print(out)

Python多进程环境下日志模块导致死锁

近期公司的一个Python程序在启动新进程的时候总是会失败。在进程里可以看到对应的进程已经创建成功,但对应代码并未执行,且没有输入任何日志。

通过定位,发现问题源自Python的logging模块,在写文件模式下,logging模块是不支持多进程的。

问题产生原因

Python默认采用Fork方式创建新进程,在Fork新进程的时候会连同 也一同复制到新进程。

  1. 当主进程里有两个线程T1/T2,以及一个锁Lock1。
  2. 线程T2获取了锁Lock1,此时线程T1创建了一个新进程P2,此时Lock1被一同frok给了P2。
  3. P2执行时尝试等待Lock1解锁。由于线程T2不会被复制到P2,没有人给P2线程的Lock1解锁,导致P2死锁。

Python的logging模块在写文件时会加锁,由于锁被复制导致进程死锁。

注:由于创建新进程时锁会被复制,混用多进程和多线程时的加锁操作应当格外小心。

解决方案

根据Python的官方文档,logging模块不支持多进程模式下将日志保存到单一日志文件。多进程模式下日志保存方案,建议参考Python官方文档 Logging to a single file from multiple processes

参考资料:

TypeScript + React.FC + Hook

Vue.js的使用更接近传统的Web开发,入门门槛比较低。同时双向数据绑定等特性也让Vue.js更为平易近人。在我看来Vue.js为易用性做的妥协在成就了Vue.js的同时,也制约了Vue.js,让他无法变得“伟大”。

在Node.js、React、Vue.js出现后,整个前端的表现能力越来越强,同时也变的越来越复杂。传统依靠jQuery的开发模式已无法支持现在大型SPA应用的开发。相比Vue.js,React这种高度组件化开发框架才更能代表今后前端的发展方向。

之前也看过一些React的相关教程。我一方面认同React的组件开发理念,另一方面又被React繁琐的开发体验劝退(Ant Design Pro早期版本里的登录实现十分劝退)。

近期有机会实际使用了React一段时间。相比初次接触React,现在的TypeScript + React.FC + Hook似乎才是React的完全形态。

React的高度组件化,让代码结构很自然的变的清晰(当然,过细的拆分也让人头痛)。TypeScript让很多潜在错误可以在编译阶段被发现,而且编辑器也开始变的智能很多。Hook的引入,彻底释放了React.FC的能力。相比Class Components使用Function Components的代码实现要简洁很多。

企业信息化建设随想

信息化在一个企业中的地位,要不就应当很高,要不就可有可无,不存在中间状态。

企业信息化实际上是以信息化为手段,将企业的管理思想进行落地。企业信息化的过程应当伴随这企业管理流程的梳理和优化,企业的信息化部门在一定程度上是一个管理决策部门。如果做不这一点,信息化部门就完全沦为了后台职能部门,做些简单的系统运维工作。

一个优秀的IT经理应当熟悉公司的业务流程、熟悉当前最新的技术动态、还要有能力争取到足够的资源来推动信息化建设。

Carrot Box流程管理平台

django-lb-workflow 我开发的一个Django流程引擎APP。设计之初是以使用便捷性为目标,自带了完整的模板,希望可以方便的集成到已有系统。尽管已经将django-lb-workflow做到尽量的易用,但距离真正的开箱即用还有一段距离。

Carrot Box是一个完整的Django易用,带了权限管理、部门、角色等必要模块,真正的做到开箱即用。通过对Carrot Box的定制可以方便的改造为OA、工单系统、CRM等业务系统。

Carrot Box的主要特点:

  • 是一个完整的应用,可以直接跑起来,开箱即用。
  • 自带HR模块,支持部门、角色的定义。支持按照部门、角色设置权限。
  • 带了几个范例流程,方便熟悉系统。
  • 包含一个代码生成器的使用范例,用于熟悉如果快速的创建一个自定义流程。
  • simplewf模块使用范例,以纯配置的方式添加新流程。

Carrot Box范例站点

之前的django-lb-workflow范例站点已经切换到 Carrot Box

地址: http://wf.haoluobo.com/

管理员账号:admin 密码:password

切换为其他用户: http://wf.haoluobo.com/impersonate/search

退回管理员账号: http://wf.haoluobo.com/impersonate/stop

将Carrot Box跑起来:

make init-pyenv
make init
make run