蚊章汇 - 软件开发
type: channel
link: https://linuxcn.bwsl.wang/category/1019/
items: 人工智能教程（七）：Scikit-learn 和训练第一个模型
items: 人工智能教程（六）：Keras 和第一个数据集
items: Git 提交是差异、快照还是历史记录？
items: Textual：为 Python 增加漂亮的文本用户界面（TUI）
items: 利用 Vely 在 Linux 构建你自己的 SaaS
items: 解读那些令人困惑 Git 术语
items: 人工智能教程（三）：更多有用的 Python 库
items: Git 分支：直觉与现实
items: 使用 dialog 和 jq 在 Linux 上编写高效终端 TUI
items: 12-Factor 应用方法论的开源开发者指南

人工智能教程（七）：Scikit-learn 和训练第一个模型
type: item
date: 2024-01-31T23:34:18+08:00
description: 在本系列的 上一篇文章 中，我们用 TensorFlow 构建了第一个神经网络，然后还通过 Keras 接触了第一个数据集。在本系列的第七篇文章中，我们将继续探索神经网络，并使用数据集来训练模型。我们还将介绍另一个强大的机器学习 Python 库 scikit-learn。不过在进入正题之前，我要介绍两个轰动性的人工智能应用：ChatGPT 和 DALL-E 2。（LCTT 译注：此文原文发表于 2023 年初，恰值以 ChatGPT 为代表的 AI 热潮开始掀起。）OpenAI 是一个人工智能研究实验室，它在人工智能和机器学习领域做了很多研究。 埃隆·马斯克 Elon Musk 是该组织的联合创始人之一。2022 年 11 月，该实验室推出了一款名为 ChatGPT 的在线工具。它是一个可以像人类一样聊天的人工智能聊天机器人。它是使用监督学习和强化学习技术训练的 大型语言模型 large language model （LLM）。ChatGPT 使用了 OpenAI 的 GPT-3.5 语言模型，这是 GPT-3（ 生成式预训练变换器 Generative Pre-trained Transformer ）的改进版本，GPT-3 是一种使用深度学习来生成类似人类文本的语言模型。（LCTT 译注：OpenAI 已于 2023 年 3 月 14 日 发布了 GPT-4.0，它支持图文混合的输入输出，并大幅提升了推理能力和准确性。）我仍然记得第一次使用 ChatGPT 时的兴奋。它清楚地展现了人工智能的能力。ChatGPT 的回答质量很高，通常与人类给出的答案没有区别。你可以使用它来纠正语法错误、改写句子、总结段落、编写程序等。实际上，我就用 ChatGPT 改写了本文中的许多句子。此外，我还故意使用有语法错误的文本测试了 ChatGPT，它纠正后的句子非常准确。它重新措辞和总结段落的能力也很惊人。程序员甚至有可能使用 ChatGPT 在短时间内解决编程难题。在 编程探险挑战赛 2022 Advent of Code 2022 中，就有人这样宣称（LCTT 译注：比赛官方只是没有完全禁止使用人工智能作为辅助，但是并不很推崇这样的作法。消息来源）。事实上在 2022 年 12 月，也就是 ChatGPT 发布的一个月后，Stack Overflow 发布了一条新的规定，禁止提交 GPT 或 ChatGPT 生成答案。（LCTT 译注：消息来源：Temporary policy Generative AI (e.g., ChatGPT) is banned - Meta Stack Overflow）图 1 显示了 ChatGPT 编写的将两个矩阵相加的 Python 程序。我要求用 BASIC、FORTRAN、Pascal、Haskell、Lua、Pawn、C、c++、Java 等语言编写程序，ChatGPT 总能给出答案，甚至对于像 Brainfuck 和 Ook! 这样生僻的编程语言也是如此。我很确定 ChatGPT 没有从互联网上复制程序代码。更确切地说，我认为 ChatGPT 是基于对上述编程语言的语法知识生成了这些答案的，这些知识是从训练它的大量数据中获得的。许多专家和观察人士认为，随着 ChatGPT 的发展，人工智能已经成为主流。ChatGPT 的真正力量将在未来几个月或几年里被看到。OpenAI 的另一个令人惊叹的在线人工智能工具是 DALL-E 2，它以卡通机器人 WALL-E（LCTT 译注：电源《机器人总动员》中的主角）和著名画家/艺术家 萨尔瓦多·达利 Salvador Dalí 的名字命名。DALL-E 2 是一个可以根据英文描述来生成绘画的人工智能系统。该工具支持丰富的图像风格，如油画、卡通、漫画、现实主义、超现实主义、壁画等。它还可以模仿著名画家的风格，如达利、毕加索、梵高等。由 DALL-E 2 生成的图像质量非常高。我用下面的描述测试了这个工具：“一个快乐的人在海滩旁看日出的立体主义画作”。图 2 是 DALL-E 2 根据我的描述生成的图像之一。立体主义是毕加索推广的一种绘画风格。问问你的任何一个画家朋友，他/她都会说这确实是一幅立体主义风格的画。令人惊讶的是软件——它也许很复杂——能够模仿像毕加索、梵高、达利这样的大师。我强烈建议你体验一下它。这种体验将非常有趣，同时也体现了人工智能的威力。但请记住，像 ChatGPT 和 DALL-E 2 这样的工具也会带来很多问题，比如版权侵犯、学生的作业抄袭等。（LCTT 译注：本文的题图就是 DALL-E 3 生成的。）介绍 scikit-learnscikit-learn 是一个非常强大的机器学习 Python 库。它是一个采用 新 BSD 许可协议 …

人工智能教程（六）：Keras 和第一个数据集
type: item
date: 2024-01-24T17:35:18+08:00
description: 在本文中我们将继续学习概率论的知识。在本系列的 上一篇文章中，我们学习了使用 Anaconda，加强了概率论的知识。在本文中我们将继续学习概率论的知识，学习使用 seaborn 和 Pandas 进行数据可视化，并进一步介绍 TensorFlow 和 Keras 的使用。让我们从增长人工智能和机器学习的理论知识开始。众所周知人工智能、机器学习、数据科学、深度学习等是当今计算机科学的热门话题。然而，计算机科学还其他热门的话题，比如 区块链 blockchain 、 物联网 Internet of Things （IoT）、 量子计算 quantum computing 等。那么，人工智能领域的发展是否会对这些技术产生积极的影响呢？首先，让我们讨论一下区块链。根据维基百科的定义，“区块链是一种分布式账本，它由不断增长的记录（称为 区块 block ）组成，这些记录使用加密技术安全地连接在一起。”乍一看，人工智能和区块链似乎是两个高速发展的独立技术。但令人惊讶的是，事实并非如此。区块链相关的行话是 完整性 integrity ，人工智能相关的行话是数据。我们将大量数据交给人工智能程序去处理。虽然这些应用程序产生了惊人的结果，但我们如何信任它们呢？这就提出了对可解释的人工智能的需求。它可以提供一定的保证，以便最终用户可以信任人工智能程序提供的结果。许多专家认为，区块链技术可以用来提高人工智能软件做出的决策的可信度。另一方面， 智能合约 smart contract （区块链技术的一部分）可以从人工智能的验证中受益。从本质上讲，智能合约和人工智能通常都是做决策。因此，人工智能的进步将对区块链技术产生积极影响，反之亦然。下面让我们讨论一下人工智能和物联网之间的影响。早期的物联网设备通常没有强大的处理能力或备用电池。这使得需要大量处理能力的机器学习的软件无法部署在物联网设备上。当时，大多数物联网设备中只部署了基于规则的人工智能软件。基于规则的人工智能的优势在于它很简单，需要相对较少的处理能力。如今的物联网设备具备更强大的处理能力，可以运行更强大的机器学习软件。特斯拉开发的高级驾驶辅助系统 特斯拉自动驾驶仪 Tesla Autopilo 是物联网与人工智能融合的典范。人工智能和物联网对彼此的发展产生了积极影响。最后，让我们讨论人工智能和量子计算是如何相互影响的。尽管量子计算仍处于起步阶段，但 量子机器学习 quantum machine learning （QML）是其中非常重要的课题。量子机器学习基于两个概念：量子数据和量子-经典混合模型。量子数据是由量子计算机产生的数据。 量子神经网络 quantum neural network （QNN）用于模拟量子计算模型。TensorFlow Quantum 是一个用于量子-经典混合机器学习的强大工具。这类工具的存在表明，在不久的将来将会有越来越多的基于量子计算的人工智能解决方案。seaborn 入门seaborn 是一个基于 Matplotlib 的数据可视化 Python 库。用它能够绘制美观且信息丰富的统计图形。通过 Anaconda Navigator 可以轻松安装 seaborn。我用 ESPNcricinfo 网站上 T20 国际板球赛的击球记录，创建了一个名为 T20.csv 的 CSV（ 逗号分隔值 comma-separated value ）文件，其中包含以下 15 列：球员姓名、职业生涯跨度、比赛场次、局数、未出局次数、总得分、最高得分、平均得分、面对球数、击球率、百分次数、五十分次数、零分次数、四分次数和六分次数。图 1 是使用 Pandas 库读取这个 CSV 文件的程序代码。我们已经在前面介绍过 Pandas 了。下面逐行解释程序代码的作用。第 1 行和第 2 行导入 Pandas 和 seaborn 包。第 3 行从 JupyterLab 的工作目录中读取文件 T20.csv。第 4 行打印元数据和第一行数据。图 1 显示了这行数据，它显示了 T20 国际板球赛中得分最高的 Virat Kohli 的击球记录。第 5 行将元数据和 T20.csv 中的前五行数据保存到 Best5.csv 中。在执行该行代码时会在 JupyterLab 的工作目录中创建这个文件。第 6 行根据列百分次数按升序对 CSV 文件进行排序，并将前 10 名世纪得分手的详细信息存储到 Highest100.csv 中。该文件也将存储在 JupyterLab 的工作目录中。最后，第7行代码提取了第 5 列（总得分）和第 7 列（平均得分）的数据信息，并生成散点图。图 2 显示了程序在执行时生成的散点图。在程序末尾添加如下代码行并再次运行。sns.kdeplot(data=df.iloc[:, […

Git 提交是差异、快照还是历史记录？
type: item
date: 2024-01-22T02:47:00+08:00
description: 大家好！我一直在慢慢摸索如何解释 Git 中的各个核心理念（提交、分支、远程、暂存区），而提交这个概念却出奇地棘手。要明白 Git 提交是如何实现的对我来说相当简单（这些都是确定的！我可以直接查看！），但是要弄清楚别人是怎么看待提交的却相当困难。所以，就像我最近一直在做的那样，我在 Mastodon 上问了一些问题。大家是怎么看待 Git 提交的？我进行了一个 非常不科学的调查，询问大家是怎么看待 Git 提交的：是快照、差异，还是所有之前提交的列表？（当然，把它看作这三者都是合理的，但我很好奇人们的 主要 观点）。这是调查结果：结果是：51% 差异42% 快照4% 所有之前的提交的历史记录3% “其他”我很惊讶差异和快照两个选项的比例如此接近。人们还提出了一些有趣但相互矛盾的观点，比如 “在我看来，提交是一个差异，但我认为它实际上是以快照的形式实现的” 和 “在我看来，提交是一个快照，但我认为它实际上是以差异的形式实现的”。关于提交的实际实现方式，我们稍后再详谈。在我们进一步讨论之前：我们的说 “一个差异” 或 “一个快照” 都是什么意思？什么是差异？我说的“差异”可能相当明显：差异就是你在运行 git show COMMIT_ID 时得到的东西。例如，这是一个 rbspy 项目中的拼写错误修复：diff --git a/src/ui/summary.rs b/src/ui/summary.rs index 5c4ff9c..3ce9b3b 100644 --- a/src/ui/summary.rs +++ b/src/ui/summary.rs @@ -160,7 +160,7 @@ mod tests { "; let mut buf: Vec<u8> = Vec::new(); - stats.write(&mut buf).expect("Callgrind write failed"); + stats.write(&mut buf).expect("summary write failed"); let actual = String::from_utf8(buf).expect("summary output not utf8"); assert_eq!(actual, expected, "Unexpected summary output"); } 你可以在 GitHub 上看到它： https://github.com/rbspy/rbspy/commit/24ad81d2439f9e63dd91cc1126ca1bb5d3a4da5b什么是快照？我说的 “快照” 是指 “当你运行 git checkout COMMIT_ID 时得到的所有文件”。Git 通常将提交的文件列表称为 “树”（如“目录树”），你可以在 GitHub 上看到上述提交的所有文件：https://github.com/rbspy/rbspy/tree/24ad81d2439f9e63dd91cc1126ca1bb5d3a4da5b（它是 /tree/ 而不是 /commit/）“Git 是如何实现的”真的是正确的解释方式吗？我最常听到的关于学习 Git 的建议大概是 “只要学会 Git 在内部是如何表示事物的，一切都会变得清晰明了”。我显然非常喜欢这种观点（如果你花了一些时间阅读这个博客，你就会知道我 喜欢 思考事物在内部是如何实现的）。但是作为一个学习 Git 的方法，它并没有我希望的那么成功！通常我会兴奋地开始解释 “好的，所以 Git 提交是一个快照，它有一个指向它的父提交的指针，然后一个分支是一个指向提交的指针，然后……”，但是我试图帮助的人会告诉我，他们并没有真正发现这个解释有多有用，他们仍然不明白。所以我一直在考虑其他方案。但是让我们还是先谈谈内部实现吧。Git 是如何在内部表示提交的 —— 快照在内部，Git 将提交表示为快照（它存储每个文件当前版本的 “树”）。我在 在一个 Git 仓库中，你的文件在哪里？ 中写过这个，但下面是一个非常快速的内部格式概述。这是一个提交的表示方式：$ git cat-file -p 24ad81d2439f9e63dd91cc1126ca1bb5d3a4da5b tree e197a79bef523842c91ee06fa19a51446975ec35 parent 26707359cdf0c2db66eb1216bf7ff00eac782f65 author Adam Jensen <adam@acj.sh> 1672104452 -0500 committer Adam Jensen <adam@acj.sh> 1672104890 -0500 Fix typo in expec…

Textual：为 Python 增加漂亮的文本用户界面（TUI）
type: item
date: 2024-01-15T19:04:38+08:00
description: 快速入门使用 TextualPython 在 Linux 上有像 TkInter 这样的优秀 GUI（图形用户界面）开发库，但如果你不能运行图形应用程序怎么办？文本终端，并非只在 Linux 上有，而且 BSD 和其它的出色的类 Unix 操作系统上也有。如果你的代码是用 Python 编写的，你应该使用 Textual 来帮助你编写 TUI（文本用户界面）。在这个快速介绍中，我将向你展示两个你可以用 Textual 做的示例，并且介绍它未来可能的应用方向。所以 Textual 是什么？Textual 是一个为 Python 构建的快速应用程序开发框架，由 Textualize.io 构建。它可以让你用简单的 Python API 构建复杂的用户界面，并运行在终端或网络浏览器上！你需要的跟进这个教程的工具你需要有以下条件：具备基础的编程经验，最好熟练使用 Python。理解基础的面向对象概念，比如类和继承。一台安装了 Linux 与 Python 3.9+ 的机器一款好的编辑器（Vim 或者 PyCharm 是不错的选择）我尽可能简单化代码，以便你能轻松理解。此外，我强烈建议你下载代码，或至少按照接下来的说明安装相关程序。安装步骤首先创建一个虚拟环境：python3 -m venv ~/virtualenv/Textualize 现在，你可以克隆 Git 仓库并创建一个可以编辑的发布版本：. ~/virtualenv/Textualize/bin/activate pip install --upgrade pip pip install --upgrade wheel pip install --upgrade build pip install --editable . 或者直接从 Pypi.org 安装：. ~/virtualenv/Textualize/bin/activate pip install --upgrade KodegeekTextualize 我们的首个程序：日志浏览器这个 日志浏览器 就是一款简单的应用，能执行用户 PATH 路径上的一系列 UNIX 命令，并在任务执行完毕后捕获输出。以下是该应用的代码：import shutil from textual import on from textual.app import ComposeResult, App from textual.widgets import Footer, Header, Button, SelectionList from textual.widgets.selection_list import Selection from textual.screen import ModalScreen # Operating system commands are hardcoded OS_COMMANDS = { "LSHW": ["lshw", "-json", "-sanitize", "-notime", "-quiet"], "LSCPU": ["lscpu", "--all", "--extended", "--json"], "LSMEM": ["lsmem", "--json", "--all", "--output-all"], "NUMASTAT": ["numastat", "-z"] } class LogScreen(ModalScreen): # ... Code of the full separate screen omitted, will be explained next def __init__(self, name = None, ident = None, classes = None, selections = None): super().__init__(name, ident, classes) pass class OsApp(App): BINDINGS = [ ("q", "quit_app", "Quit"), ] CSS_PATH = "os_app.tcss" ENABLE_COMMAND_PALETTE = False # Do not need the command palette def action_quit_app(self): self.exit(0) def compose(self) -> ComposeResult: # Create a list of commands, valid commands are assumed to be on the PATH variable. selections = [Selection(name.title…

利用 Vely 在 Linux 构建你自己的 SaaS
type: item
date: 2023-12-12T19:09:46+08:00
description: Vely 可让你在网络应用程序中利用 C 语言的强大功能。Vely 将 C 语言的高性能和低内存占用与 PHP 等语言的易用性和安全性相结合。作为自由开源软件，它以 GPLv3 和 LGPL 3 授权，所以你甚至可以用它来构建商业软件。利用 Vely 构建 SaaS你可以使用 Vely 创建一个多租户网络应用程序，它可以作为软件即服务模式（SaaS）在互联网上运行。每个用户都有一个完全独立的数据空间。在这个网络应用程序示例中，用户可以注册一个笔记本服务来创建笔记，然后查看和删除它们。它仅用了 7 个源文件，310 行代码，就展示了如何集成多项技术:MariaDB网络浏览器ApacheUnix 套接字运作原理以下是从用户的角度来看应用程序是如何工作的。下图是代码演示。该应用允许用户通过指定电子邮件地址和密码创建新的登录名。你可以用任何你喜欢的方式设置它们，例如运用 CSS：验证用户的电子邮件:每个用户使用自己独有的用户名和密码登录：一旦登录，用户就可以添加笔记：用户可以获取笔记列表：删除笔记之前，应用会申请确认信息：用户确认后，笔记被删除：设置先决条件遵照 Vely.dev 上的安装指示。这是一个使用 DNF、APT、Pacman 或者 Zypper 等标准工具包的快速流程。由于它们都是这个范例的一部分，你必须安装 Apache 作为网络服务器，安装 MariaDB 作为数据库。安装 Vely 后，如使用 Vim，打开里面的“语法高亮显示”：vv -m 获取源代码这个演示 SaaS 应用程序的源代码是 Vely 安装的一部分。为每个应用程序创建一个单独的源代码目录不失为一个好主意（而且你可以按自己喜好命名）。在这种情况下，解包源代码会帮你完成这些工作:$ tar xvf $(vv -o)/examples/multitenant_SaaS.tar.gz $ cd multitenant_SaaS 默认情况下，该应用程序以 multitenant_SaaS 命名，但你可以将其命名为任何内容（如果这么做，其他每个地方你都需要改一下）。创建应用程序第一步是创建一个应用程序。使用 Vely 的 vf 工具就可以轻松完成：$ sudo vf -i-u $(whoami) multitenant_SaaS 这个命令创建了一个新的应用程序主目录（/var/lib/vv/multitenant_SaaS），并帮你执行应用程序设置。通常，这意味着在该主目录中创建各种子目录并分配权限。在这种情况下，只有当前用户（whoami 的结果）拥有目录，具有 0700 权限，这确保了其他人没有访问文件的权限。创建数据库在你键入任何代码之前，你需要一个能够存储该应用程序所用信息的空间。首先，创建一个名为 db_multitenant_SaaS 的 MariaDB 数据库，由用户名为 vely 的用户所有，密码为 your_password 。你可以修改刚才提到的任何值，但得记住，在这个示例里，你需要将包含这些内容的每个地方都得修改一遍。在 MySQL 中以 root 身份登录：create database if not exists db_multitenant_SaaS; create user if not exists vely identified by 'your_password'; grant create,alter,drop,select,insert,delete,update on db_multitenant_SaaS.* to vely; 然后在数据库内创建数据库对象（表，记录等等）：use db_multitenant_SaaS; source setup.sql; exit 将 Vely 连接至数据库为了让 Vely 知晓你数据库的位置以及如何登录进去，创建一个名为 db_multitenant_SaaS 的数据库配置文件。（该名称用于在源代码中的数据库声明，所以如果你改了它，确保在它存在的每个地方都改一遍。）Vely 使用原生的 MariaDB 数据库连接，因此你可以指定给定的数据库所能允许的任何选项:$ echo '[client] user=vely password=your_password database=db_multitenant_SaaS protocol=TCP host=127.0.0.1 port=3306' > db_multitenant_SaaS 构建应用程序使用 vv 工具构建应用程序，利用 --db 选项指定 MariaDB 数据库和数据库配置文件：$ vv -q--db=mariadb:db_multitenant_SaaS 启用应用服务器启动你的网络应用程序的服务器，需要使用 vf Fas…

解读那些令人困惑 Git 术语
type: item
date: 2023-12-08T04:07:12+08:00
description: 我正在一步步解释 Git 的方方面面。在使用 Git 近 15 年后，我已经非常习惯于 Git 的特性，很容易忘记它令人困惑的地方。因此，我在 Mastodon 上进行了调查：你有觉得哪些 Git 术语很让人困惑吗？我计划写篇博客，来解读 Git 中一些奇怪的术语，如：“分离的 HEAD 状态”，“快速前移”，“索引/暂存区/已暂存”，“比 origin/main 提前 1 个提交”等等。我收到了许多有洞见的答案，我在这里试图概述其中的一部分。下面是这些术语的列表：HEAD 和 “heads”“分离的 HEAD 状态”在合并或变基时的 “ours” 和 “theirs”“你的分支已经与 'origin/main' 同步”HEAD^、HEAD~、HEAD^^、HEAD~~、HEAD^2、HEAD~2.. 和 ...“可以快速前移”“引用”、“符号引用”refspecs“tree-ish”“索引”、“暂存的”、“已缓存的”“重置”、“还原”、“恢复”“未跟踪的文件”、“追踪远程分支”、“跟踪远程分支”检出reflog合并、变基和遴选rebase –onto提交更多复杂的术语我已经尽力讲解了这些术语，但它们几乎覆盖了 Git 的每一个主要特性，这对一篇博客而言显然过于繁重，所以在某些地方可能会有一些粗糙。HEAD 和 “heads”有些人表示他们对 HEAD 和 refs/heads/main 这些术语感到困惑，因为听起来像是一些复杂的技术内部实现。以下是一个快速概述：“heads” 就是 “分支”。在 Git 内部，分支存储在一个名为 .git/refs/heads 的目录中。（从技术上讲，官方 Git 术语表 中明确表示分支是所有的提交，而 head 只是最近的提交，但这只是同一事物的两种不同思考方式）HEAD 是当前的分支，它被存储在 .git/HEAD 中。我认为，“head 是一个分支，HEAD 是当前的分支” 或许是 Git 中最奇怪的术语选择，但已经设定好了，想要更清晰的命名方案已经为时已晚，我们继续。“HEAD 是当前的分支” 有一些重要的例外情况，我们将在下面讨论。“分离的 HEAD 状态”你可能已经看到过这条信息：$ git checkout v0.1 You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by switching back to a branch. [...] （消息译文：你处于 “分离 HEAD” 的状态。你可以四处看看，进行试验性的更改并提交，你可以通过切换回一个分支来丢弃这个状态下做出的任何提交。）这条信息的实质是：在 Git 中，通常你有一个已经检出的 “当前分支”，例如 main。存放当前分支的地方被称为 HEAD。你做出的任何新提交都会被添加到你的当前分支，如果你运行 git merge other_branch，这也会影响你的当前分支。但是，HEAD 不一定必须是一个分支！它也可以是一个提交 ID。Git 会称这种状态（HEAD 是提交 ID 而不是分支）为 “分离的 HEAD 状态”例如，你可以通过检出一个标签来进入分离的 HEAD 状态，因为标签不是分支如果你没有当前分支，一系列事情就断链了：git pull 根本就无法工作（因为它的全部目的就是更新你的当前分支）除非以特殊方式使用 git push，否则它也无法工作git commit、git merge、git rebase 和 git cherry-pick 仍然可以工作，但它们会留下“孤儿”提交，这些提交没有连接到任何分支，因此找到这些提交会很困难你可以通过创建一个新的分支或切换到一个现有的分支来退出分离的 HEAD 状态在合并或变基中的 “ours” 和 “theirs”遇到合并冲突时，你可以运行 git checkout --ours file.txt 来选择 “ours” 版本中的 file.txt。但问题是，什么是 “ours”，什么是 “theirs” 呢？我总感觉此类术语混淆不清，也因此从未用过 git checkout --ours，但我还是查找相关资料试图理清。在合并的过程中，这是如何运作的：当前分支是 “ours”，你要合并进来的分支是 “theirs”，这样看来似乎很合理。$ git checkout merge-into-ours # 当前分支是 “ours” $ git merge fro…

人工智能教程（三）：更多有用的 Python 库
type: item
date: 2023-12-03T05:42:48+08:00
description: 在本系列的 上一篇文章 中，我们回顾了人工智能的历史，然后详细地讨论了矩阵。在本系列的第三篇文章中，我们将了解更多的矩阵操作，同时再介绍几个人工智能 Python 库。在进入主题之前，我们先讨论几个人工智能和机器学习中常用的重要术语。 人工神经网络 artificial neural network （通常简称为 神经网络 neural network ，NN）是机器学习和深度学习的核心。顾名思义，它是受人脑的生物神经网络启发而设计的计算模型。本文中我没有插入神经网络模型的图片，因为在互联网上很容易找到它们。我相信任何对人工智能感兴趣的人应该都见过它们，左边是输入层，中间是一个或多个隐藏层，右边是输出层。各层之间的边上的 权重 weight 会随着训练不断变化。它是机器学习和深度学习应用成功的关键。 监督学习 supervised learning 和 无监督学习 unsupervised learning 是两个重要的机器学习模型。从长远来看，任何立志于从事人工智能或机器学习领域工作的人都需要学习它们，并了解实现它们的各种技术。这里我认为有必要简单说明两种模型之间的区别了。假设有两个人分别叫 A 和 B，他们要把苹果和橘子分成两组。他们从未见过苹果或橘子。他们都通过 100 张苹果和橘子的图片来学习这两种水果的特征（这个过程称为模型的训练）。不过 A 还有照片中哪些是苹果哪些是橘子的额外信息（这个额外信息称为标签）。这里 A 就像是一个监督学习模型，B 就像是无监督学习模型。你认为在是识别苹果和橘子的任务上，谁的效果更好呢？大多数人可能会认为 A 的效果更好。但是根据机器学习的理论，情况并非总是如此。如果这 100 张照片中只有 5 张是苹果，其它都是橘子呢？那么 A 可能根本就不熟悉苹果的特征。或者如果部分标签是错误的呢？在这些情况下，B 的表现可能比 A 更好。在实际的机器学习应用中会发生这样的情况吗？是的！训练模型用的数据集可能是不充分的或者不完整的。这是两种模型都仍然在人工智能和机器学习领域蓬勃发展的众多原因之一。在后续文章中，我们将更正式地讨论它们。下面我们开始学习使用 JupyterLab，它是一个用于开发人工智能程序的强大工具。JupyterLab 入门在本系列的前几篇文章中，为了简单起见，我们一直使用 Linux 终端运行 Python 代码。现在要介绍另一个强大的人工智能工具——JupyterLab。在本系列的第一篇文章中，我们对比了几个候选项，最终决定使用 JupyterLab。它比 Jupyter Notebook 功能更强大，为我们预装了许多库和包，并且易于团队协作。还有一些其它原因，我们将在后续适时探讨它们。在本系列的第一篇文章中，我们已经学习了如何安装 JupyterLab。假设你已经按文中的步骤安装好了 JupyterLab，使用 jupyter lab 或 jupyter-lab 命令在会默认浏览器（如 Mozilla Firefox、谷歌 Chrome 等）中打开 JupyterLab。（LCTT 译注：没有安装 JupyterLab 也不要紧，你可以先 在线试用 JupyterLab）图 1 是在浏览器中打开的 JupyterLab 启动器的局部截图。JupyterLab 使用一个名为 IPython（交互式 Python）的 Python 控制台。注意，IPython 其实可以独立使用，在 Linux 终端运行 ipython 命令就可以启动它。现阶段我们使用 JupyterLab 中的 Jupyter Notebook 功能。点击图 1 中用绿框标记的按钮，打开 Jupyter Notebook。这时可能会要求你选择内核。如果你按照本系列第一篇的步骤安装 JupyterLab，那么唯一的可选项就是 Python 3（ipykernel）。请注意，你还可以在 JupyterLab 中安装其它编程语言的内核，比如 C++、R、MATLAB、Julia 等。事实上 Jupyter 的内核相当丰富，你可以访问 Jupyter 内核清单 了解更多信息。下面我们快速了解一下 Jupyter Notebook 的使用。图 2 显示的是一个在浏览器中打开的 Jupyter Notebook 窗口。从浏览器标签页的标题可以看出，Jupyter Notebook 打开的文件的扩展名是 .ipynb。在图 2 处可以看到有三个选项，它们表示 Jupyter Notebook 中可以使用的三种类型的单元。“Code”（绿色框） 表示代码单元，它是用来执行代码的。“Markdown” 单元可用于输入说明性的文本。如果你是一名计算机培训师，可以用代码单元和 Markdown 单元来创建交互式代码和解释性文本，然后…

Git 分支：直觉与现实
type: item
date: 2023-12-01T08:41:12+08:00
description: 你好！我一直在投入写作一本关于 Git 的小册，因此我对 Git 分支投入了许多思考。我不断从他人那里听说他们觉得 Git 分支的操作方式违反直觉。这使我开始思考：直觉上的分支概念可能是什么样，以及它如何与 Git 的实际操作方式区别开来？在这篇文章中，我想简洁地讨论以下几点内容：我认为许多人可能有的一个直觉性的思维模型Git 如何在内部实现分支的表示（例如，“分支是对提交的指针”）这种“直觉模型”与实际操作方式之间的紧密关联直觉模型的某些局限性，以及为何它可能引发问题本文无任何突破性内容，我会尽量保持简洁。分支的直观模型当然，人们对分支有许多不同的直觉。我自己认为最符合“苹果树的一个分支”这一物理比喻的可能是下面这个。我猜想许多人可能会这样理解 Git 分支：在下图中，两个红色的提交就代表一个“分支”。我认为在这个示意图中有两点很重要：分支上有两个提交分支有一个“父级”（main），它是这个“父级”的分支虽然这个观点看似合理，但实际上它并不符合 Git 对于分支的定义 — 最重要的是，Git 并没有一个分支的“父级”的概念。那么，Git 又是如何定义分支的呢？在 Git 里，分支是完整的历史在 Git 中，一个分支是每个过去提交的完整历史记录，而不仅仅是那个“分支”提交。因此，在我们上述的示意图中，所有的分支（main 和 branch）都包含了 4 次提交。我创建了一个示例仓库，地址为：https://github.com/jvns/branch-example。它设置的分支方式与前图一样。现在，我们来看看这两个分支：main 分支包含了 4 次提交：$ git log --oneline main 70f727a d f654888 c 3997a46 b a74606f a mybranch 分支也有 4 次提交。最后两次提交在这两个分支里都存在。$ git log --oneline mybranch 13cb960 y 9554dab x 3997a46 b a74606f a 因此，mybranch 中的提交次数为 4，而不仅仅是 2 次“分支”提交，即 13cb960 和 9554dab。你可以用以下方式让 Git 绘制出这两个分支的所有提交：$ git log --all --oneline --graph * 70f727a (HEAD -> main, origin/main) d * f654888 c | * 13cb960 (origin/mybranch, mybranch) y | * 9554dab x |/ * 3997a46 b * a74606f a 分支以提交 ID 的形式存储在 Git 的内部，分支会以一种微小的文本文件的形式存储下来，其中包含了一个提交 ID。这就是我一开始提及到的“技术上正确”的定义。这个提交就是分支上最新的提交。我们来看一下示例仓库中 main 和 mybranch 的文本文件：$ cat .git/refs/heads/main 70f727acbe9ea3e3ed3092605721d2eda8ebb3f4 $ cat .git/refs/heads/mybranch 13cb960ad86c78bfa2a85de21cd54818105692bc 这很好理解：70f727 是 main 上的最新提交，而 13cb96 是 mybranch 上的最新提交。这样做的原因是，每个提交都包含一种指向其父级的指针，所以 Git 可以通过追踪这些指针链来找到分支上所有的提交。正如我前文所述，这里遗漏的一个重要因素是这两个分支间的任何关联关系。从这里能看出，mybranch 是 main 的一个分支——这一点并没有被表明出来。既然我们已经探讨了直观理解的分支概念是如何不成立的，我接下来想讨论的是，为何它在某些重要的方面又是如何成立的。人们的直观感觉通常并非全然错误我发现，告诉人们他们对 Git 的直觉理解是“错误的”的说法颇为流行。我觉得这样的说法有些可笑——总的来说，即使人们关于某个题目的直觉在某些方面在技术上不精确，但他们通常会有完全合理的理由来支持他们的直觉！即使是“不正确的”模型也可能极其有用。现在，我们来讨论三种情况，其中直觉上的“分支”概念与我们实际在操作中如何使用 Git 非常相符。变基操作使用的是“直观”的分支概念现在，让我们回到最初的图片。当你在 main 上对 mybranch 执行 变基 rebase 操作时，它将取出“直观”分支上的提交（只有两个红色的提交）然后将它们应用到 main 上。执行结果就是，只有两次提交（x 和 y）被复制。以下是相关操作的样子：$ git switch mybranch $ git rebase main $ git…

使用 dialog 和 jq 在 Linux 上编写高效终端 TUI
type: item
date: 2023-11-18T15:15:00+08:00
description: 为何选择文字用户界面（TUI）？许多人每日都在使用终端，因此， 文字用户界面 Text User Interface （TUI）逐渐显示出其价值。它能减少用户输入命令时的误差，让终端操作更高效，提高生产力。以我的个人使用情况为例：我每日会通过家用电脑远程连接到我使用 Linux 系统的实体 PC。所有的远程网络连接都通过私有 VPN 加密保护。然而，当我需要频繁重复输入命令进行连接时，这种经历实在令人烦躁。于是，我创建了下面这个 Bash 函数，从而有所改进：export REMOTE_RDP_USER="myremoteuser" function remote_machine() { /usr/bin/xfreerdp /cert-ignore /sound:sys:alsa /f /u:$REMOTE_RDP_USER /v:$1 /p:$2 } 但后来，我发现自己还是频繁地执行下面这条命令（在一行中）：remote_pass=(/bin/cat/.mypassfile) remote_machine $remote_machine $remote_pass 这太烦了。更糟糕的是，我的密码被明文存储在我的电脑上（我虽然使用了加密驱动器，但这点依然令人不安）。因此，我决定投入一些时间，编写一个实用的脚本，从而更好地满足我的基本需求。我需要哪些信息才能连接到远程桌面？实际上，要连接到远程桌面，你只需提供少量信息。这些信息需要进行结构化处理，所以一个简单的 JSON 文件就能够满足要求：{"machines": [ { "name": "machine1.domain.com", "description": "Personal-PC" }, { "name": "machine2.domain.com", "description": "Virtual-Machine" } ], "remote_user": "MYUSER@DOMAIN", "title" : "MY COMPANY RDP connection" } 尽管在各种配置文件格式中，JSON 并非最佳选择（例如，它不支持注解），但是 Linux 提供了许多工具通过命令行方式解析 JSON 内容。其中，特别值得一提的工具就是 jq。下面我要向你展示如何利用它来提取机器列表：/usr/bin/jq --compact-output --raw-output '.machines[]| .name' \ $HOME/.config/scripts/kodegeek_rdp.json) \ "machine1.domain.com" "machine2.domain.com" jq 的文档可以在 这里 找到。另外，你也可以直接将你的 JSON 文件复制粘贴到 jq play，试用你的表达式，然后在你的脚本中使用这些表达式。既然已经准备好了连接远程计算机所需的所有信息，那现在就让我们来创建一个美观实用的 TUI 吧。Dialog 的帮助Dialog 是那些你可能希望早些认识的、被低评估的 Linux 工具之一。你可以利用它构建出一个井然有序、简介易懂，并且完美适用于你终端的用户界面。比如，我可以创建一个包含我喜欢的编程语言的简单的复选框列表，且默认选择 Python：dialog --clear --checklist "Favorite programming languages:" 10 30 7\ 1 Python on 2 Java off 3 Bash off 4 Perl off 5 Ruby off 我们通过这条命令向 dialog 下达了几个指令：清除屏幕（所有选项都以 -- 开头）创建一个带有标题的复选框（第一个位置参数）决定窗口尺寸（高度、宽度和列表高度，共 3 个参数）列表中的每条选项都由一个标签和一个值组成。惊人的是，仅仅一行代码，就带来了简洁直观和视觉友好的选择列表。关于 dialog 的详细文档，你可以在 这里 阅读。整合所有元素：使用 Dialog 和 JQ 编写 TUI我编写了一个 TUI，它使用 jq 从我的 JSON 文件中提取配置详细信息，并且使用 dialog 来组织流程。每次运行，我都会要求输入密码，并将其保存在一个临时文件中，脚本使用后便会删除这个临时文件。这个脚本非常基础，但它更安全，也使我能够专注于更重要的任务 ?那么 脚本 看起来是怎样的呢？下面是代码：#!/bin/bash # Author Jose Vicente Nunez # Do not use this script on a public computer. It is not secure... # https://invisible-island.ne…

12-Factor 应用方法论的开源开发者指南
type: item
date: 2023-11-16T20:45:22+08:00
description: 这 12 项基本原则能够帮助团队快速高效地构建高度可扩展的应用程序12-Factor 应用方法论 为在短时间内构建应用程序并使其具有可扩展性提供了指导。它由 Heroku 的开发人员创建，用于软件即服务（SaaS）应用程序、网络应用程序以及可能的通信平台即服务（CPaaS）。在有效组织项目和管理可扩展应用程序方面，12 要素应用程序方法论对开源开发具有强大的优势。12-Factor 应用方法论的原则12-Factor 应用方法论的规则非常严格，也是开发和部署 SaaS 应用程序的基石，并且不受任何编程语言或数据库的限制。1：一份基准代码，多份部署每个应用程序都应该有一个具有多个不同环境/部署的代码库。开发人员不应仅仅为了在不同环境中设置而开发另一个代码库。不同的环境代表不同的状态，但这些不同的环境应该共享同一个代码库。在许多开源项目都存储在 GitLab 这样的版本控制系统中的情况下，一个环境可以被视为一个分支。例如，你可以在任何中央版本控制系统中为名为 VoIP-app 的云 VoIP 应用程序创建一个单独的存储库，然后创建两个分支：开发分支（development）和暂存分支（staging），并将主分支（master）作为发布分支。2：明确声明和隔离依赖关系应声明所有依赖关系。你的应用程序可能会依赖外部系统工具或库，但不应对系统工具或库有任何 隐含的 依赖。你的应用程序必须始终明确声明所有依赖关系及其正确版本。在代码库中包含依赖关系可能会产生问题，特别是在开源项目中，外部库的更改可能会将错误引入代码库。例如，代码库可能会使用一个外部库，但没有明确声明该依赖关系或版本。如果外部库更新到更新的、未经测试的版本，这可能会与你的代码产生兼容性问题。如果明确声明了依赖关系及其正确版本，你的代码库就不会出现这种问题。根据技术栈的不同，最好使用软件包管理器，通过读取代表依赖库名称和版本的依赖库声明清单，在各自的系统上下载依赖库。3：在环境中存储配置当需要支持多个环境或客户端时，配置就成了应用程序的重要组成部分。不同部署之间的配置应存储在环境变量中。这样就可以在部署之间轻松更改配置，而无需更改代码。对于闭源应用程序来说，这一原则是有益的，因为你不会希望数据库连接信息或其他秘密数据等敏感信息被公开。然而，在开放源代码开发中，这些细节都是公开的。在这种情况下，好处是你不需要反复修改代码。你只需这样设置变量，只需改变环境，就能让代码完美运行。4：把后端服务当作附加资源所有后备服务（如数据库、外部存储或消息队列）都被视为附加资源，由执行环境附加或分离。根据这一原则，如果这些服务的位置或连接细节发生变化，仍无需更改代码。这些细节可以在配置中找到。备份服务可以从部署中快速附加或分离。例如，如果基于云的电子表格的数据库无法正常工作，开发人员应该能够创建一个从最近备份恢复的新数据库服务器，而无需对代码库进行任何更改。5：严格分离构建和运行12-Factor 应用方法论要求严格区分构建、发布和运行阶段。第一阶段是 构建 阶段。在这一阶段，源代码被组装或编译成可执行文件，同时加载依赖关系并创建资产。每次需要部署新代码时，构建阶段就会开始。第二阶段是 发布 阶段。在此阶段，构建阶段生成的代码与部署的当前配置相结合。最终发布的版本包含构建和配置，可在执行环境中立即执行。第三个阶段是 运行 阶段，也是最后阶段：应用程序在执行环境中运行。该阶段不应被其他任何阶段打断。通过严格区分这些阶段，我们可以避免代码中断，使系统维护更加易于管理。6：以一个或多个无状态进程运行应用应用程序作为一个或多个进程的集合在执行环境中执行。这些进程是无状态的，其持久化数据存储在数据库等后台服务中。这对开源非常有用，因为使用某版本应用程序的开发人员可以在其云平台上创建多节点部署，以实现可扩展性。数据不会在其中持久化，因为如果其中任何一个节点崩溃，数据就会丢失。7：通过端口绑定提供服务你的应用程序应作为独立的服务，独立于其他应用程序。它它应该能通过URL供其他服务访问，以服务形式存在。这样，你的应用程序就可以在需要时作为其他应用程序的资源。利用这一概念，你可以构建 REST API。8：通过进程模型进行扩展该原则也称为并发原则，它表明应用程序中的每个进程都应能够自我扩展、重启或克隆。开发人员可以创建多个进程，并将应用程序的负载分配给这些进程，而不是将一个进程变大。通过这种方法，你可以将每种工作负载分配给一个进程类型，从而构建能处理不同工作负载的应用程序。9：快速启动和优雅终止以增强健壮性你的应用应当基于简单的进程构建，因此开发者可以放大进程的同时还能在发生问题时重启它们。这使得应用的进程易于丢弃。根据这一原则构建应用程序意味着代码的快速部署、快速弹性扩展、更灵活的发布流程以及稳健的生产部署。…