UCD-ECS150-计算机科学笔记-全-

UCD ECS150 计算机科学笔记(全)

001:安装软件 🛠️

在本节课中,我们将学习如何为Python编程设置计算环境。核心内容包括:理解计算环境的概念、学习使用包管理器Pixie来安装和管理软件,以及掌握在命令行中导航和操作的基本技能。这些技能对于后续的Python学习至关重要。

什么是计算环境? 💻

计算环境是指您计算机上用于运行代码或程序的所有硬件、软件和其他配置的总和。您需要能够设置计算环境,以确保可以运行所需的代码。同时,当您与协作者合作时,也需要能够告知他们您所使用的软硬件配置,以便他们也能运行您的代码。

有时,您可能需要为特定项目指定特定版本的软件。例如,Python 2和Python 3之间存在巨大差异。在本课程中,我们将使用Python 3,但您可能会遇到需要旧版Python 2的工具。因此,您需要能够根据项目需求安装不同的版本。

此外,您可能在没有管理员权限的计算机上工作(例如,在UC Davis的高性能计算服务器上)。在这种情况下,您需要一种无需管理员权限即可安装软件的方法。

如何管理计算环境? 📦

您将使用一种称为包管理器环境管理器的工具来下载和安装软件。例如,Mac用户可能听说过Homebrew,Windows用户可能使用过Windows Store。这些工具可以帮助您安装软件并跟踪不同版本。

今天,我们将学习一个名为Pixie的包管理器,它可以在任何操作系统(Windows、Linux、Mac)上安装。使用Pixie,我们可以运行相同的命令来安装软件和Python。Pixie还能帮助您更新包、管理不同项目中安装的包以及不同版本的包,从而确保您可以控制计算机的软件环境。

Pixie这类包管理器还能帮助我们创建虚拟环境。虚拟环境允许您在同一台计算机上并行安装多个独立的软件环境。例如,您可以在一个虚拟环境中使用Python 2,在另一个虚拟环境中使用Python 3。这样,您可以将不兼容的软件放在各自的虚拟环境中,实现并行安装。

对于研究人员来说,不同的研究项目可能需要不同的软件集,而这些软件可能彼此不兼容。因此,我们需要一种方法来安装所有这些软件。

为什么选择Pixie? 🐍

有多种环境管理器可供选择,但我们将使用Pixie。Pixie是一个相对较新的环境管理器,主要专注于Python,但也可以与R一起使用。它可以安装R、R包、Python、Python包以及其他许多软件。

如果您以前使用过Conda,Pixie与Conda兼容,并且可以为您安装Conda包。Pixie可以安装各种开源软件。

Python的包生态系统 🌐

Python拥有成千上万的包。这些包通常发布在名为PyPI的网站上。目前,PyPI上有超过644,000个社区贡献的包。这些包的质量参差不齐,但数量庞大,可供您免费使用。

如果您是R用户,可能对CRAN很熟悉。CRAN是R的等效平台,但Python的包数量通常是CRAN的两到三倍。因此,Python非常流行,是全球最受欢迎的编程语言之一,通常每年都位列前三。

这些包让您能够用Python完成各种任务。例如,如果您想用Python进行数学计算,Python内置的数学库可能不适合矩阵乘法等操作。这时,您可以使用像NumPy这样的包来高效地进行矩阵和向量运算。同样,如果您想进行图像处理或音频文件处理,也有相应的包可用。对于数据科学,如果您习惯在R中使用数据框,Python也有几个等效的包。

除了PyPI,我们还可以从Conda Forge获取包。Conda Forge与PyPI类似,但它不仅限于Python,还分发各种软件。例如,有用于R的Conda包、用于R包的Conda包,甚至用于文本编辑器等计算机软件的Conda包。

Pixie允许我们安装这两种包:Conda包和PyPI包。

安装Git Bash(仅限Windows用户) ⬇️

如果您是Windows用户,您需要一个名为Git Bash的工具。Git本身是一个出色的版本控制工具,但我们在本课程中不会讨论它。然而,Git安装时会附带Git Bash,这是一个类似于终端的工具。因为Windows自带的终端在研究计算方面存在许多问题,效果不佳。我们希望每个人都能运行相似或相同的命令,而在Windows上,您需要安装第三方软件,不能仅使用内置工具。

请访问网站 git-scm.com。您应该会看到一个类似的网站。点击“Download for Windows”按钮进行下载。下载完成后,找到下载的文件,双击并运行安装程序。您只需保持所有默认设置即可。关键是要确保安装过程中包含了Git Bash(这通常是自动的,但如果您更改了默认设置,请确保不要取消勾选安装Git Bash的选项)。

如果您在安装过程中遇到问题,请举手或贴上红色便签,我们会提供帮助。

安装Pixie 🚀

现在,我们将为所有人安装Pixie。请访问Pixie官方网站 pixie.sh(读者材料中也有链接)。向下滚动直到找到“Installation”部分。根据您的操作系统选择相应的安装说明。

  • Windows用户:下载安装程序并运行它。
  • MacOS或Linux用户:打开终端程序(MacOS自带“终端”,无需额外安装),复制提供的命令,并将其粘贴到终端中运行。这个命令会下载并运行安装程序。

如果任何人在这些步骤中遇到困难或出现错误信息,请举手或贴上红色便签。我们将等待大约三到五分钟,让大家完成安装。我和其他助教会四处走动,尽力帮助大家。

命令行基础导航 🗺️

现在,假设大家都已经安装了终端(和Pixie)。我们将快速熟悉一下终端,因为大多数人都表示以前没有真正使用过终端。

终端可能看起来类似这样,具体外观可能因布局和字体而略有不同。您会看到一个提示符,可以在那里输入命令。我们已经输入过 pixie info 命令。这是您使用文本命令与计算机通信的方式。我们喜欢使用文本命令,因为它们具有很高的可重复性。我可以复制并粘贴文本命令给其他人,他们可以运行相同的命令。而如果通过点击图形界面操作,则较难分享和复现,容易出错。

终端的工作方式很像您计算机上的文件浏览器(在Windows上叫“资源管理器”,在Mac上叫“Finder”)。当您打开文件浏览器时,它会显示您当前所在的目录(文件夹)。终端也是如此,它会告诉您当前所在的目录及其路径。

您可能会在提示符中看到一个波浪号 ~。这表示您位于主目录中,这是计算机存储您所有个人文件的目录。不同操作系统中主目录的具体位置可能不同。

以下是一些基本命令:

  • ls:列出当前目录中的文件和文件夹。
  • pwd:显示当前目录的完整路径。
  • cd:更改目录。例如,cd Documents 进入“Documents”文件夹。cd .. 返回上一级目录。cd 后不加任何内容会直接返回主目录。

尝试这些命令,看看您是否能识别终端当前所在的位置。使用 pwd 查看完整路径,使用 ls 查看文件。

创建项目目录并初始化Pixie项目 📁

Pixie的理念是为每个项目设置独立的安装或环境。因此,一个好的习惯是为每个项目创建一个新目录,并将所有相关内容放在该目录中。这样,您就不会丢失文件或将文件分散在计算机各处。

我们将使用Pixie来管理该目录中的所有软件。首先,确保您在主目录(使用 cd 命令返回)。然后,使用 mkdir 命令创建一个新目录。mkdir 代表“创建目录”。

mkdir pixie-workshop

然后,进入这个新目录:

cd pixie-workshop

现在,我们使用Pixie初始化一个新项目:

pixie init my_project

运行后,您会看到Pixie创建了一个名为“my_project”的文件夹。Pixie将在这个文件夹中安装我们想要使用的所有软件。进入该项目目录:

cd my_project

使用 ls 命令查看,您会看到一个名为 pixie.toml 的文件。这是Pixie用于存储该项目所有设置的文件。它将记录您选择安装的所有包。如果您想与他人分享项目,以便他们能安装相同的软件,这个文件是最重要的文件之一。同样,对于未来的您(例如,几个月后当您忘记使用了什么软件时),这个文件也至关重要。

理解 pixie.toml 文件 📄

让我们查看 pixie.toml 文件的内容。我们可以使用 nano 文本编辑器:

nano pixie.toml

文件内容大致如下:

  • authors:项目作者列表。您可以在此处添加您的姓名和电子邮件。
  • channels:我们稍后会讨论。
  • name:项目名称,与目录名相同。
  • platforms:列出您希望项目运行的操作系统。默认是您当前计算机的操作系统。如果您想在多个操作系统(例如Linux和Mac)上运行项目,可以在此添加。这有助于确保您安装的包在所有目标平台上都可用。
  • version:项目版本号(可选)。
  • dependencies:列出所有已安装的包。目前是空的,因为我们还没有安装任何东西。

要退出 nano,按 Ctrl+X。如果文件有更改,它会询问是否保存,按 Y 表示是,然后按 Enter 确认。

使用Pixie安装软件(以Python为例) 🐍

现在,让我们实际安装一些软件。首先,确认当前没有安装任何包:

pixie list

输出应为“No packages are found”。

接下来,安装Python。使用 pixie add 命令来为项目添加依赖:

pixie add python

Pixie会下载Python及其所有依赖项。安装完成后,再次运行 pixie list,您会看到许多已安装的包,其中Python会以粗体或绿色显示,表示这是您显式安装的包。

现在,我们可以在Pixie环境中运行Python了。有两种方法:

  1. 使用 pixie run

    pixie run python
    

    这将启动Python交互式环境。您可以输入 print("Hello") 进行测试。要退出Python,可以输入 exit() 或按 Ctrl+D

  2. 使用 pixie shell(在Mac/Linux上更常用,Windows的Git Bash可能有bug):

    pixie shell
    

    这会激活Pixie环境,之后您可以直接输入 python 等命令。要退出该shell,输入 exit

管理包版本和项目环境 🔄

pixie.toml 文件中的依赖项可以指定版本范围。例如,Python的条目可能显示为 >=3.13,<3.14,这表示需要3.13.x版本,但不包括3.14。这确保了兼容性。

您可以手动编辑 pixie.toml 文件来更改版本约束。例如,将Python版本改为 >=3.8,<3.9。保存文件后,运行 pixie install,Pixie会重新解析文件并安装指定的版本。

要移除一个包,使用 pixie remove 命令:

pixie remove python

为下午的课程设置环境 ⚙️

为了准备下午的Python课程,我们需要安装一些额外的包。请运行以下命令:

pixie add python numpy jupyter polars plot9

这将安装Python、用于数值计算的NumPy、用于笔记本编辑的Jupyter、用于数据框的Polars以及用于绘图的Plot9。安装可能需要一些时间。

安装完成后,您可以通过运行以下命令来测试Jupyter的增强型Python环境:

pixie run ipython

理解 pixie.lock 文件 🔒

安装软件后,您会注意到目录中多了一个 pixie.lock 文件。与 pixie.toml 文件(指定版本范围)不同,pixie.lock 文件记录了确切的版本信息以及每个包的下载URL。这个文件主要是给计算机读的。

当您分发项目时,应该同时包含 pixie.tomlpixie.lock 文件。如果接收者有 pixie.lock 文件,他们运行 pixie install 时,Pixie会尝试精确复现相同的环境。

如果由于平台差异等原因导致基于 pixie.lock 的安装失败,可以尝试删除 pixie.lock 文件,然后仅使用 pixie.toml 运行 pixie install。这样Pixie会重新解析依赖并安装,可能会获得更灵活、兼容的版本。

从PyPI安装包 🌉

默认情况下,pixie add 从Conda Forge安装包。如果您需要从PyPI安装包,可以添加 -p 标志:

pixie add -p package_name

通常建议优先使用Conda Forge,但如果某个包在Conda Forge上不可用或有问题,可以使用此方法从PyPI安装。

总结 📝

在本节课中,我们一起学习了如何为Python编程设置计算环境。我们介绍了计算环境的概念,学习了使用Pixie包管理器来安装和管理软件,掌握了命令行导航的基础知识,并创建了带有特定依赖项的Pixie项目。我们还了解了 pixie.tomlpixie.lock 文件的作用,以及如何从不同来源安装包。

这些技能是进行可重复研究计算的基础。请确保在午餐期间完成所有必要的安装,以便为下午的Python学习做好准备。如果您遇到任何问题,请随时向我们寻求帮助。

002:命令行基础与文本编辑

在本节课中,我们将学习如何使用命令行界面与计算机交互。我们将从基本概念开始,了解命令行与图形界面的区别,然后学习核心的导航和文件操作命令,最后介绍如何在命令行中使用文本编辑器。


概述:计算机的另一种视角

计算机环境由多个抽象层级构成,从底层的二进制(0和1)开始,逐步构建到我们熟悉的图形用户界面(GUI)。命令行界面(CLI)是另一种与计算机交互的方式,它不使用桌面、窗口等图形隐喻,而是通过文本指令进行操作。理解这一点有助于消除对命令行的陌生感和畏惧感。


导航命令:探索你的计算机

上一节我们介绍了命令行的基本概念,本节中我们来看看如何通过命令行在计算机的文件系统中移动和查看信息。

查看当前位置与内容

打开命令行后,默认界面信息很少。要查看当前目录下的内容,我们使用 ls 命令。

ls

执行此命令会列出当前目录中的所有文件和文件夹,类似于在图形界面中打开一个文件夹窗口。

获取详细信息

ls 命令可以配合标志(flag)使用,以获取更多信息。例如,ls -l 会以长格式列出详细信息,包括文件权限、所有者、大小和修改时间。

ls -l

查看隐藏文件

在计算机中,以点(.)开头的文件通常是配置文件,默认情况下不会显示。使用 ls -a 命令可以查看所有文件,包括这些隐藏文件。

ls -a

获取命令帮助

如果你不确定某个命令的用法,可以使用 man 命令查看其手册页。

man ls

要退出手册页视图,按 q 键。

理解文件路径

要了解你当前在计算机文件系统中的确切位置,使用 pwd(打印工作目录)命令。

pwd

输出结果是一个从根目录(/)开始的路径字符串,显示了你的当前位置。

切换目录

要进入另一个目录,使用 cd(更改目录)命令。

cd Desktop

命令执行后通常没有明显提示,但你可以通过提示符的变化或再次使用 pwd 来确认位置已改变。

使用特殊符号导航

命令行中有两个重要的特殊符号:

  • ~(波浪号):代表你的家目录(home directory)。
  • /(斜杠):代表根目录(root directory)。

你可以使用它们快速导航:

  • cd ~ 可以随时返回家目录。
  • cd / 可以进入根目录。

每个目录中还包含两个特殊的符号条目:

  • .(单点):代表当前目录。
  • ..(双点):代表上一级目录。

例如,在 Desktop 目录中,使用 cd .. 可以返回其父目录。

绝对路径与相对路径

  • 绝对路径:从根目录(/)开始的完整路径。例如:/Users/Tyler/Desktop。无论你在哪里,使用绝对路径都能直接到达目标。
  • 相对路径:相对于你当前位置的路径。例如,在家目录时,Desktop 就是一个相对路径。

使用绝对路径可以让你从任何位置直接跳转到目标,这是命令行高效的原因之一。

处理文件名中的空格

文件名中的空格在命令行中会造成歧义,因为空格通常用于分隔命令和参数。如果必须处理带空格的文件名,需要使用反斜杠(\)来“转义”空格。

cd My\ Documents

更佳实践是避免在文件名和目录名中使用空格,可以用下划线(_)或连字符(-)代替。


文件操作命令:创建、移动与删除

上一节我们学习了如何在文件系统中导航,本节中我们来看看如何创建、复制、移动和删除文件和目录。

以下是几个核心的文件操作命令:

创建目录

使用 mkdir 命令创建新目录(文件夹)。

mkdir my_first_directory

创建文件

有多种方法可以创建文件。一种简单的方法是使用 echo 命令将文本输出重定向到一个新文件。

echo "Hello World" > hello.txt

这行命令创建了一个名为 hello.txt 的文本文件,内容为 “Hello World”。

移动文件或目录

使用 mv(移动)命令来移动文件或目录。其语法是:mv <源> <目标>

mv hello.txt my_first_directory/

这会将 hello.txt 文件移动到 my_first_directory 文件夹中。

复制文件

使用 cp(复制)命令来复制文件。语法是:cp <源> <目标>

cp my_first_directory/hello.txt my_first_directory/hello_copy.txt

删除文件

使用 rm(删除)命令来删除文件。请谨慎使用此命令,因为它通常不会将文件移至回收站,而是直接永久删除。

rm my_first_directory/hello.txt

要删除目录及其所有内容,需要使用 -r(递归)标志。

rm -r my_first_directory

命令行文本编辑:Vim 入门

上一节我们掌握了文件的基本操作,本节中我们来看看如何在命令行中直接编辑文本文件的内容,这将使用一个名为 Vim 的文本编辑器。

为什么使用命令行文本编辑器?

像 Microsoft Word 这样的图形化编辑器会在文件中保存大量格式和元数据。而在数据处理和编程中,我们通常需要纯净的文本文件。命令行文本编辑器(如 Vim)功能强大,尤其在远程服务器或没有图形界面的环境中必不可少。

打开与编辑文件

使用 vim 命令后接文件名来打开文件。

vim new_hello.txt

Vim 有几种模式,最常用的是:

  • 普通模式:打开文件后的默认模式,用于执行命令、导航。
  • 插入模式:用于输入和编辑文本。

从普通模式按 i 键进入插入模式,此时可以开始编辑文本。编辑完成后,按 Esc 键返回普通模式。

保存与退出

在普通模式下进行保存和退出操作:

  1. 输入 : 进入命令模式。
  2. 输入 w(write)并回车以保存文件。
  3. 输入 q(quit)并回车以退出 Vim。

你也可以将保存和退出合并为一个命令:在普通模式下输入 :wq 并回车。


总结与练习

本节课中我们一起学习了命令行的基础知识。我们了解了命令行是与图形界面平行的另一种计算机交互方式,学习了使用 lscdpwd 进行导航,使用 mkdirmvcprm 进行文件操作,并初步接触了使用 Vim 编辑器在命令行中编辑文本。

为了巩固所学,我们准备了一个小练习。请下载练习文件,按照其中的指引,运用今天学到的命令来完成一系列任务。通过实践,你会对命令行的使用更加熟悉和自信。

003:版本控制基础

在本节课中,我们将要学习版本控制的基本概念,并动手实践使用Git进行本地版本控制。我们将从安装Git开始,逐步学习如何创建仓库、跟踪文件、提交更改、查看历史记录以及使用分支功能。

概述:什么是版本控制?

在开始动手之前,我们先来理解一下版本控制。你是否曾在电脑上见过类似“文档_v1.docx”、“文档_v2_final.docx”、“文档_v3_really_final.docx”这样的文件?这是一种非常常见的手动版本管理方式。

然而,这种方式存在几个问题:

  1. 文件名中能存储的元数据(如日期、修改者)非常有限。
  2. 我们无法快速了解每个版本具体修改了什么内容,必须打开文件才能知道。
  3. 当项目包含多个文件时,这种管理方式会迅速变得混乱不堪。

版本控制系统(VCS)就是为了解决这些问题而生的。它通过软件帮助我们系统地组织、跟踪和管理文件的不同版本。Git是当前最流行的版本控制系统,它既可以在本地独立工作,也可以与远程服务器(如GitHub)协同,实现团队协作。

第一步:安装与验证Git

为了顺利进行后续操作,我们需要确保每个人的电脑上都安装了Git。

以下是检查Git是否已安装的步骤:

  • 在Mac上:打开“应用程序” -> “实用工具”中的“终端”应用,输入命令 git --version 并回车。如果已安装,会显示版本号;如果未安装,会提示命令未找到。
  • 在Windows上:在开始菜单中搜索“Git Bash”。如果找到并打开它,输入 git --version 命令检查。如果找不到Git Bash,则可能需要安装。

如果检查发现尚未安装Git,请根据你的操作系统(Windows或Mac),按照课程资料中的安装指南完成安装。安装过程中如有问题,可以随时在聊天区提问。

第二步:创建你的第一个Git仓库

安装好Git后,我们就可以开始实践了。首先,我们需要创建一个用于练习的目录,并将其初始化为Git仓库。

上一节我们介绍了如何安装Git,本节中我们来看看如何创建并初始化一个本地仓库。

  1. 打开你的命令行工具(Mac终端或Windows的Git Bash)。
  2. 使用 cd ~ 命令确保进入你的用户主目录。
  3. 使用 mkdir intro_to_git 命令创建一个名为“intro_to_git”的新目录。
  4. 使用 cd intro_to_git 命令进入这个新目录。
  5. 现在,使用 git init 命令将这个目录初始化为一个Git仓库。你会看到提示“Initialized empty Git repository...”。
  6. 运行 ls -a 命令,你会发现多了一个名为 .git 的隐藏文件夹,这就是Git存储所有版本历史和数据的地方。

此时,你已经创建了一个空的Git仓库。可以运行 git status 命令查看仓库的当前状态,它会显示你位于默认的 master 分支,并且还没有任何提交。

第三步:跟踪文件与提交更改

拥有一个空仓库后,我们接下来要学习如何让Git跟踪我们的文件变化。这需要理解Git工作的几个核心区域:工作目录、暂存区(Staging Area)和仓库。

以下是Git工作的基本流程:

  1. 工作目录:你实际编辑文件的地方。
  2. 暂存区:你通过 git add <文件名> 命令,将想要纳入下次版本(提交)的文件快照放入的区域。
  3. 仓库:你通过 git commit 命令,将暂存区的内容永久保存为一个新版本的地方。

现在,让我们按照这个流程来操作:

  1. intro_to_git 目录中,使用文本编辑器(如Vim)创建一个新文件。例如,运行 vi test.txt
  2. i 键进入插入模式,输入一些内容,例如“Hello, world. This is my test git file.”。
  3. Esc 键退出插入模式,然后输入 :wq 并回车,保存文件并退出编辑器。
  4. 运行 git status。Git会提示有一个“Untracked files”(未跟踪的文件)test.txt,并建议使用 git add 来开始跟踪它。
  5. 运行 git add test.txt 将文件添加到暂存区。
  6. 再次运行 git status,你会看到文件状态变为“Changes to be committed”(等待提交的更改)。
  7. 现在,运行 git commit 来提交这个版本。这会打开你的默认文本编辑器(如Vim)让你输入提交信息。
  8. 在编辑器中,按 i 进入插入模式,输入一条有意义的提交信息,例如“Created new file with base content.”。然后按 Esc,输入 :wq 保存并退出。
  9. 提交完成后,运行 git status 会显示“working tree clean”(工作区干净)。运行 git log 可以查看提交历史,其中包含了刚才的提交记录、作者、时间和你的提交信息。

记住一个原则:频繁提交,并撰写清晰的提交信息。 这能让你在未来轻松回顾和理解每个版本的改动。

第四步:理解与使用分支

分支是Git一个非常强大的功能,它允许你在一个独立的“平行时空”里开发新功能或尝试修改,而不会影响主线(通常是 master 分支)的稳定性。

上一节我们学会了提交更改,本节中我们来看看如何使用分支来管理不同的开发线。

  1. 首先,确保你在 master 分支(git status 会显示)。然后运行 git checkout -b working。这个命令创建并切换到一个名为 working 的新分支。
  2. 运行 git branch 可以查看所有分支,当前所在分支前会有一个 * 号。
  3. 现在你在 working 分支上。修改 test.txt 文件(比如修正一个拼写错误),然后 git addgit commit 这个修改。
  4. 你还可以在 working 分支上创建一个新文件 test2.txt,同样添加并提交它。
  5. 运行 git log 查看,你会发现提交历史在 working 分支上延伸了。
  6. 现在,运行 git checkout master 切换回 master 分支。运行 ls 命令,你会发现 test2.txt 文件消失了!这是因为 test2.txt 只存在于 working 分支的提交中,Git会根据你当前所在的分支来呈现文件系统。
  7. 再次运行 git checkout working 切换回去,文件又回来了。

分支的核心概念是隔离。每个分支都是项目在某个时间点的一个独立发展线,它们之间的修改在合并前互不影响。

第五步:合并分支与解决冲突

当你在一个分支(如 working)上完成了功能开发并测试通过后,通常需要将其合并回主分支(如 master)。

以下是合并分支的基本步骤:

  1. 首先,切换到你想合并的目标分支,通常是 mastergit checkout master
  2. 然后,运行合并命令,指定要合并来自哪个分支:git merge working
  3. 如果两个分支修改了不同的文件或同一文件的不同部分,Git会自动进行“快速向前合并”,你会看到合并成功的提示。
  4. 运行 git logls,可以看到 master 分支现在包含了 working 分支的所有更改。

然而,如果两个分支都修改了同一文件的同一行,就会产生冲突。Git无法自动决定保留哪个修改,需要你手动解决。

  1. 假设在 working 分支和 master 分支上,你都修改了 test.txt 文件的同一行,但内容不同。
  2. master 分支上执行 git merge working 时,Git会提示“CONFLICT”(冲突)并告知哪个文件有问题。
  3. 用编辑器打开冲突文件,你会看到类似这样的标记:
    <<<<<<< HEAD
    这是master分支上的内容
    =======
    这是working分支上的内容
    >>>>>>> working
    
  4. 你需要手动编辑文件,决定保留哪一部分内容(或者进行整合),并删除这些 <<<<<<<=======>>>>>>> 标记。
  5. 解决冲突后,使用 git add <文件名> 将文件标记为已解决冲突,然后执行 git commit 来完成这次合并提交。

总结与资源

本节课中我们一起学习了版本控制的基础知识和Git的核心操作。我们涵盖了:

  • 版本控制的概念:解决了手动管理版本的各种弊端。
  • Git安装与仓库初始化:使用 git init 创建本地仓库。
  • 文件跟踪与提交:通过 git addgit commit 记录文件版本,并强调提交信息的重要性。
  • 分支管理:使用 git branch, git checkout 创建和切换分支,实现并行开发。
  • 合并与冲突解决:使用 git merge 合并分支,并学会如何处理合并冲突。

要熟练使用Git,关键在于多练习。你可以尝试在自己的项目中使用Git来管理代码、文档或数据。

以下是一些有用的学习资源:

  • Git Cheat Sheet(速查表):一份命令速查指南,非常适合新手随时查阅。
  • Pro Git 书籍:一本开源、详尽的Git指南,深入讲解了所有概念和操作。
  • 进阶学习:关注后续关于“Git团队协作”或“GitHub使用”的课程,学习如何将本地仓库与远程服务器连接,实现备份和团队合作。

恭喜你完成了Git的入门学习!现在,你可以开始用更高效、更可控的方式来管理你的所有项目了。

004:GitHub团队协作入门

概述

在本节课中,我们将学习如何使用GitHub进行团队协作。GitHub是一个基于Git的在线平台,广泛用于软件开发和版本控制,尤其适合团队研究项目。我们将练习如何设置、共享并协作处理一个代码库或项目,涵盖从本地到云端的完整工作流程。


课程背景与准备

大家好,欢迎来到“面向GitHub的可复现研究”工作坊。我是Pamela Reynolds,加州大学戴维斯分校数据科学与信息学数据实验室的副主任。今天我将主持本次工作坊,并由数据实验室的其他几位成员作为讲师和助手协助。稍后我会介绍他们。

对于不熟悉数据实验室的学员,我们是一个由教务长和图书馆资助的项目,旨在促进数据科学方法、技术和最佳实践的应用,以增强所有领域的研究和学习。今天我们有来自许多不同学科的研究人员,欢迎你们。我们认为今天讨论的内容对你们当前的学习旅程以及未来在加州大学戴维斯分校之后的发展都将非常有用。

数据实验室提供研究服务,包括办公时间。如果对今天的内容有疑问,可以参加我们每周举行的Zoom办公时间。本季度和下一季度我们还有许多其他工作坊,稍后我会在聊天中发布一个链接,以便大家了解更多即将举办的活动。

今天的工作坊将涵盖使用GitHub的基础知识。GitHub是一个用于软件开发和版本控制的在线平台,在研究团队中非常流行。即使你独自一人,也可以高效地使用它,它有助于提高项目的可复现性、透明度并备份项目。

我们将练习设置、共享并协作处理一个存储库,可以将其视为托管在本地和云端的代码库或项目。

提醒一下,本工作坊是为已经熟悉使用Git和命令行界面的学习者设计的。如果你是命令行和Git的新手,可能会在跟进时遇到一些困难。我会在聊天中粘贴关于这两个内容领域的材料链接。如果你完全是新手,我们建议你先从那里开始,然后再回来参加这个GitHub工作坊。但如果你觉得需要更多练习,也欢迎今天以演示的形式观看。

此外,我们还有一些工作坊前的准备活动。你需要设置好GitHub账户,准备好GitHub用户名并链接SSH密钥。如果你还没有完成,现在可以开始做。但如果遇到问题需要帮助解决,可能需要参加我们的办公时间。

最后,对于今天在场的加州大学戴维斯分校研究生和博士后,本次工作坊符合加州大学戴维斯分校微证书途径的资格。如果你有兴趣注册该途径并想了解更多关于研究生途径计划的信息,我会在聊天中粘贴链接。在会议结束时,会有一个调查链接,请务必完成。这有助于我们获取反馈,以持续改进工作坊、开发新内容,并继续免费为大家提供。

现在,我很高兴介绍我们的讲师。今天的主讲是Wesley Brooks,他是数据实验室的数据科学家,拥有统计学博士学位。我们的助手包括Tyler Shomaker,他实际上起草了本工作坊阅读材料的第一版,因此对此了如指掌,今天他将在聊天中为大家提供帮助。Tyler是我们的博士后,拥有英语博士学位。如果你好奇英语专业如何成为数据科学家,我相信Tyler会很乐意与你聊聊这两个领域之间有趣且重要的重叠部分。我们还有Oliver Koss,他拥有计算机科学博士学位,并且在整个职业生涯中一直在使用命令行和版本控制工具。如果你需要说服自己为什么要这样做,他会很乐意与你深入探讨。

接下来,我将把时间交给Wesley,让他开始我们的课程。


课程正式开始与初步检查

好的,谢谢Pamela。在我们继续之前,我想确认一下大家是否能清楚地听到我。也许现在可以给我一个竖起大拇指的确认,好的,很好。

我们有三位出色的助手,Pamela、Tyler和Oliver,他们将在整个聊天过程中保持活跃。因此,我希望当你们遇到问题时,可以向他们提问,我们会一起解决。

最重要的一点是,我们将使用GitHub,这是一个促进协作研究的工具,也用于协作软件开发。它是一个云工具,许多人可以访问,因此你需要设置一个账户。

今天早上,你们应该都收到了加入名为“October 2022”团队的邀请,该团队已被添加到一个我们今天将共同协作的存储库中。希望你们已经看到并回应了邀请,这样就能参与该部分的活动。如果没有,现在可以做,或者在演示期间的任何时间做。我们肯定要到下半部分才会进行那部分活动。

完全参与所需的条件之一是,你已经在计算机上创建了SSH密钥,并将该密钥或其公钥版本复制到你的GitHub账户,以便GitHub能够验证你的计算机已连接到你的账户。

在之前的电子邮件中有关于如何操作的说明。但如果你最终没有完成,聊天中有一些助手,希望他们能指导你完成步骤。在某些计算机上操作更简单,如果你使用的是Windows计算机,尤其需要使用Git Bash命令行,它与Windows控制台命令行是分开的,尽管它们看起来几乎一样。

因为我们在工作坊中要使用的工具和命令仅在Git Bash命令行中有效。

这些是初步事项,我知道信息量很大。但提前提出来,希望能解决一些可能随后出现的问题。我只是想确保我们都在同一页面上,能够跟上并尽可能多地学习今天工作坊的内容。

好的,那么开始吧。抱歉,我现在要做一件事,就是把屏幕共享从这张幻灯片切换到我的命令行界面。这样,在本次工作坊过程中,我将在命令行中输入命令,然后你们应该能够逐行跟随。当然,需要做一些调整,因为我的名字不是你的名字,我的电子邮件地址也不是你的电子邮件地址,但本质上,你们将逐行跟随我输入命令行的内容。我将按照一个脚本进行,你们也可以跟随这个脚本,以防迷路。

这是该脚本的网址:UC Davis Datalab.Github.io/workshop_git_for_teams。简单的方法是直接点击链接。Wesley把它贴在了聊天中,哦,太好了,已经贴到聊天里了,这让事情变得简单方便。所以,如果你点击那个链接,就会打开显示我们今天工作坊将要遵循步骤的脚本。

实际上,也许我没有,我会找到正确的链接。

在开始之前,在我走得太远之前,我还想确认一下大家是否能舒适地阅读我共享的屏幕。文字够大吗?例如,是的,好的,我从团队那里得到了一些点头确认,这很好。

这是我的命令行界面,我将回到我的主目录,现在我们可以开始讨论什么是GitHub了。


什么是GitHub?

好的。前提条件之一是大家已经学习了Git的基础知识。Git是一个版本控制工具,可以在本地机器上运行,帮助你在开发软件或研究项目、撰写论文的过程中跟踪对文件所做的所有更改。

GitHub是一个使用Git进行版本控制的在线软件开发平台。

在本工作坊中,我们将练习为一个研究项目设置、共享并协作处理一个存储库。

那么,GitHub与Git有什么不同?最大的区别在于,Git是一个工具,可用于操作本地机器上或远程的存储库。而GitHub只是远程存储库的一个存放地点。

我想补充一点,我认为GitHub就是云端。GitHub是另一个服务器,你可以利用它来访问相同的代码信息,无论你在哪台本地机器上工作,只要它连接到那个云端,连接到那个GitHub。

没错,所以它是一个托管Git存储库的云服务。有很多其他云服务可以存储文件,但GitHub的特殊之处在于它专门存储Git存储库的文件。

你可以使用计算机上的Git通过命令行与这个网站进行交互。

因此,你可以做所有你通常在Git工作坊中学到的、在本地计算机上用Git做的所有事情,比如创建一个新的存储库、提交、分支、合并。所有这些你在Git工作坊中学到的操作,现在都可以通过连接到托管在GitHub上的这个云账户的Git来完成。

它作为云账户的特殊之处在于,现在同一个文件夹(即Git存储库)可以有一个团队的所有成员协作开发一个项目。在我们的场景中,这个项目将是一个研究项目,我们可能有数据、分析和正在撰写的论文。其他人可能认为这是一个软件项目,你正在开发一个应用程序,里面有很多代码文件。无论如何,Git工具与存储位置GitHub交互,使协作开发工作变得非常顺畅。

除了作为存储这些文件的地方,GitHub还是一个通信平台。不像社交网络,也不像电子邮件,而是一种不同类型的平台,GitHub启用了专门面向协作开发的通信渠道。

我这里打开了GitHub,也许演示比讲述更容易,展示而不是讲述。我们有诸如“Issues”这样的功能。在这里,我可以创建问题,用于与团队沟通。当然,不应该只是像“嗨,团队”这样随意的东西,而应该是像“我们需要学习使用GitHub”这样的内容。

我们还有通过GitHub共享消息的能力。

我们还有以README文件形式存在的文档。README文件在GitHub中具有特殊作用,它们会变成任何项目的主页。

还有称为“Pull Requests”的功能,这是一种高级的Git合并形式,整个项目的新分支可以完成工作,然后通过消息将其合并到主分支中。

哦,对了,GitHub还提供了创建Wiki的工具。每个项目都附带一个Wiki,当然在这个基本项目中是空白的。每个项目都附带项目板,当然这个基本项目也是空白的。但无论如何,有很多方法可以让人们使用GitHub就他们正在开发的项目进行沟通。

我想插一句,Wesley现在展示的是我们今天的沙盒。这是一个你可以玩耍的存储库。这是一种与云端交互的方式,你可以通过浏览器进行,但我们将通过命令行向你展示如何实际与之交互。所有这些不同的区域,这些Issues、Pull Requests,都是管理你开发的方式。我们称之为软件开发,但它也可以是你们一起写论文,一起协作,所以在这种情况下,它不仅仅用于代码。我们今天无法涵盖GitHub的每一个功能,但如果你想知道如何设置项目板或如何使用Wiki等具体细节,我们可以为你连接其他一些很棒的教程。所以,今天再次强调,重点真的是让你入门:创建存储库、提交、拉取、合并,做你需要做的操作,以便与你正在进行的云端工作共享。

没错。

一旦你回应了加入“October 2022”团队的请求,你将能够访问这个存储库,并尝试我一直在谈论的所有那些工具。在接下来的几个小时里,我将指导你们完成其中的几个。我想快速问一下,如果你收到了电子邮件并且已经准备好加入UC Davis团队,请给出一个绿色的勾选标记。如果你没有这样做,不知道我们在说什么,或者感到困惑,请给出一个红色的X,我们会联系你。

好的,我看到一个红色的X。如果你今天只是以演示模式跟随,请给出一个绿色的勾选标记,这样我们就不会打扰你。

我想,Tyler,实际上我们中的任何人都应该有权限将人员添加到“October 2022”团队。我会继续演示,希望后台的助手之一能够解决这个问题。

好的,在开始深入进行一些工作之前,我想提到的关于GitHub的最后一点初步事项是:由于这是一个云服务,有些文件不应该被推送到GitHub。

其中一些是像非常大的文件,GitHub有存储限制,它根本不会让你推送像几GB大小的文件。

但还有一些更微妙的问题。例如,这个存储库可以被公开查看,所以如果你在这里放了一个包含密码或敏感信息的文件,你就不想把它发布到可以公开查看的存储库中。

考虑到这一点,适合用GitHub跟踪的内容包括代码、文档,然后是像Makefile这样的脚本,也许还有一些像小图片这样的支持媒体。但我们通常不把数据放在GitHub上。数据通常包含敏感信息,但也通常不是那种需要用Git跟踪的东西,因为如果你在做可复现的研究,数据应该生成一次并且不改变。

然后对数据的任何修改都可以通过脚本来完成,这样如果你需要运行数据的不同版本,你可以通过运行脚本来完成,而不是知道当前保存的文件是更新版本还是原始版本。

因此,我们确保数据只创建一次,并通过本地共享,例如通过电子邮件或访问正确的数据存储来共享。

然后我们使用GitHub来跟踪项目文件,如代码、Makefile和文档。

当然,是的,像API密钥、密码和个人身份信息这样的敏感信息应该保存在不会被GitHub跟踪的文件之外。


账户设置与SSH密钥配置

好的。现在,为了使用GitHub,你需要创建一个账户。我知道这是前提条件的一部分,所以与其逐步讲解如何创建GitHub账户,我想做我们之前做过的事情,使用绿色勾选/红色X系统。

我想我们有能力清除人们之前的选择,或者……好吧,不管怎样,我正在查看参与者列表,以确保每个人都给出一个绿色的勾选标记,表明你已经创建了GitHub账户。

是的,我想我们刚刚让大约三个人被添加进来。好的,很好。

接下来要讨论的是我们如何与Git交互。如果你从我们的工作坊学习了Git,你已经学会了如何在命令行使用Git,这就是我们今天要做的。

还有一个图形界面叫做GitHub Desktop,这是一个连接到GitHub的程序,允许你使用点击界面与项目进行大量交互。

我们在数据实验室不喜欢以这种方式教学,因为你学习与Git交互的方式将取决于你安装的特定图形界面版本。无论你是在Windows、Mac还是Linux上,那个程序看起来都不同,你学习的系统,比如点击哪里、工具应该做什么,在不同的操作系统之间工作方式不同。

因此,我们喜欢确保你使用命令行学习Git,因为事实上,所有这些图形工具都只是……嗯……一种简化方式,执行那些相同的命令行命令。就像你点击一个按钮,它被转换成类似git clone某个存储库的命令。所以我们直接教你命令,而不是图形界面。

Wesley,请允许我简单说一下我们这样做的另一个原因。如果你进行任何类型的远程计算,你将没有屏幕可以工作,没有GUI。例如,校园计算集群上没有GitHub的桌面应用程序。所以,如果你想进行任何需要大量计算或使用他人计算机的远程工作,根本无法使用这些桌面应用程序。因此,命令行在这些领域占主导地位,学习如何使用我们今天将要介绍的工具以及过去几个工作坊中介绍的工具,从那个空间本身开始,真的非常重要,因为如果你想做那些类型的任务,你将在那里“生活”。这是一个很好的观点,谢谢。

好的。现在,关于使用Git的一点是,当你使用Git时,你希望确保你的名字被正确地归因于你所做的工作,尤其是在团队合作时,这样团队就知道是谁在做某些文件的工作,并知道在发生冲突(比如合并冲突,这是我们稍后要讨论的概念)时该找谁讨论。

为了让Git将你的姓名和电子邮件地址附加到你所做的工作上,你需要用你的姓名和电子邮件地址配置Git。这将是我们今天工作坊中第一次进入命令行。

我们将检查我们的Git配置,以确保它知道将什么姓名和电子邮件地址附加到你所做的任何工作上。

方法是使用这个命令:git config --global。注意--global之间没有空格。首先我想检查的是user.name。好的,我已经正确设置了Git,知道我的名字。我还想检查git config --global user.email。好的,Git也知道我的电子邮件地址。

Wesley,这里有个问题,那个电子邮件应该与你GitHub账户关联的电子邮件匹配吗?实际上这并不重要,这只是为了信息目的。所以Git不要求它与任何东西匹配,这只是为了知道谁做了什么,因为在协作项目中,你需要能够跟踪谁做了什么工作。

我……不是每个人都可能配置了Git来知道他们是谁。所以,如果你在这里得到的响应是空白的,我快速向你展示如何设置这些变量。你只需输入相同的命令,然后在后面跟上你想要输入的值。例如,如果我想把我的名字从Wesley改为West,我会说git config --global user.name "Westby"

好的,为了确保更改生效,我会再次输入git config --global user.name。我们看到更改已注册。希望你们都为自己输入了一个有意义的名称和一个有意义的电子邮件地址。这是团队合作的重要组成部分。

现在,这是在人类层面上跟踪谁做了什么工作,这些是人类可读的值。就机器而言,GitHub知道你的计算机连接到你的账户的方式是通过关联你的SSH密钥。在你的计算机上创建一个SSH密钥,并将其复制到GitHub。

我知道我已经提到过这一点,但我们正接近需要将我们的账户附加到我们名字的阶段,所以我想确保我们都完成了这个步骤。

你知道你已经完成这个步骤的方法是,检查你的计算机上是否有一个名为.ssh的隐藏文件夹(如果你已经做了的话)。所以我会使用ls命令列出主目录下的.ssh位置。

我有一个密钥,然后这个以.pub结尾的是我的公钥。known_hosts与我们现在的操作无关。

如果你遵循了不同的说明集,你的密钥可能不叫id_rsa,可能叫类似id_ed25519这样的名字。这没关系,不重要。重要的是你同时拥有一个密钥和一个公钥。

而这个公钥是你想要复制到GitHub的那个。所以我会通过查看公钥文件来复制它,我使用less函数。

所以,我再次输入这个位置,从我的主目录,进入隐藏文件夹.ssh,然后我想从那里获取一个特定的文件,id_rsa.pub来获取公钥版本。好的,这一串随机的字母和数字是将我的计算机与我的GitHub账户关联起来的唯一标识符。所以我需要确保这个被复制。

Wesley,这是工作坊前说明的一部分,所以我想我们是否可以继续前进。让大家看到这个很好,但我知道这是前提条件的一部分,而且我知道大多数人已经完成了,我只是想确保每个人都完成了,因为如果你没有完成,你将无法继续。

所以,是的,我不会详细讲解创建你的SSH密钥,但只是确保如果你点击GitHub页面右上角的图标,进入设置,你会在左侧看到一个菜单,其中一个项目叫做“SSH and GPG Keys”。你需要至少有一个密钥附加到你的账户,并且它需要是你刚从公钥文件中复制出来的那个。

对我来说就在这里。你需要完成这个步骤才能继续。如果我要创建一个新密钥(我不会,因为我的已经关联了),我会点击这个绿色的“New SSH key”并复制这个字符串。然后就可以了,你的账户就与你的计算机关联起来了。

哦,是的,抱歉,还有一个确认它是否有效的方法。我按q退出less屏幕。为了确认它有效,有一个快速的命令行命令。那就是尝试通过SSH连接到GitHub。SSH是一个远程终端程序。所以如果我尝试连接到GitHub,它应该告诉我它识别了我的计算机,但不允许连接。只要我们得到那个消息,我们就知道我们已经连接上了。

方法是:ssh -T git@github.com。它说“你好,你已经成功通过身份验证,但GitHub不提供shell访问权限。”这条消息是成功消息,它告诉我我已经成功验证身份。这就是这里最重要的。如果你能做到这一点,那么你就能够从命令行使用你的GitHub账户。


创建本地Git存储库并连接到GitHub

好的,所有初步事项都已解决,我们现在将开始在本地计算机上创建一个新的Git存储库,不是在GitHub上,而是在我们的计算机上。我们最好都在主目录中创建这个,这样最容易找到。如果你还记得数据实验室命令行工作坊,你可以用~键进入主目录。

在创建这个新的Git存储库之前,我将使用cd ~命令切换到主目录。cd ~将我带到主目录。

在我的主目录中,我将使用mkdir命令创建一个新文件夹。我将其命名为my_first_github_repo

现在我将切换到刚刚创建的文件夹:cd my_first_github_repo。这些都是希望从你参加数据实验室工作坊学习Git基础使用时复习过的内容。

要将这个文件夹变成Git存储库,我们进行初始化。所以,我们进入那个文件夹,然后输入git init

我们得到消息,说我们在这个位置创建了一个空的Git存储库。当然,你的会有点不同,因为你的账户路径不会是/users/wbrooks

好的,现在我们在一个Git存储库中工作。在Git存储库中最基本的事情是向README文件添加一行文本,所以让我们就这么做。在命令行中,创建带有文本行的文件的简单方法是使用echo函数,然后使用右尖括号。

首先我们放入文本,所以是echo 'Hello GitHub!'。我想使用单引号,因为放入感叹号然后双引号,你的计算机可能无法识别它应该是什么。

然后我将使用尖括号重定向输出。我将加宽我的屏幕,以便所有内容都在一行上。

好的,现在我将输入ls来列出这个文件夹的内容,我们看到我们有一个名为README.md的文件。如果我想查看该文件的内容,我可以使用less函数:less README.md。我们看到该文件的唯一内容是“Hello GitHub!”。好的,在less中,你可以按q退出,回到命令行。

现在我们已经创建了那个文件,但我们还没有告诉Git开始跟踪它。所以,再次回顾我们的基本Git命令,我们想将该文件添加到Git跟踪中:git add README.md。在我们将该文件添加到跟踪之后,我们现在需要提交我们所做的所有更改。虽然只有一个更改,但我们仍然需要提交它。我们需要一个有意义的提交消息,所以我会说“I created a repo and added a readme.”。嗯,我拼错了“readme”,但我将就一下。

好的,现在到目前为止,我们所做的只是我们在基础学习使用Git工作坊中做的相同过程,或者如果你从其他来源学习Git,我们正在做你知道的使用命令行的基本Git命令。但我们还没有做任何关于GitHub的事情,还没有任何东西将我们所做的连接到远程存储库。

所以,这样做的方法,这是我们第一次与基本Git工作流程不同的地方,是去GitHub。我将点击小章鱼猫图标进入我的主页。

然后在我的主页上,我将在“Recent repositories”旁边点击“New”,以在我的GitHub账户上创建一个新的空白存储库。

希望你们都在跟随,唯一的区别是你们不是在wbrooks下创建这个存储库,而是在你们为这个工作坊设置的GitHub账户下创建。天哪,我们想叫它什么?好吧,我们不妨匹配我们在命令行上为存储库起的名字,那是什么?是my_first_github_repo

好的,那将是GitHub存储库名称:my_first_github_repo。然后在描述中,我们可以说“Created during the Datalab workshop.”。

然后有一系列选择需要做,GitHub为你提供了一些有用的默认值。

第一个选择是你希望这个存储库是公开的还是私有的。公开是默认设置,适合大多数存储库。要创建私有存储库,嗯,实际上我不确定免费账户此时是否能够创建私有存储库。但无论如何,我们今天要做的任何事情都不敏感,没有理由不公开,所以我将把这个文件设为公开。

下一个选择是我们是否希望让GitHub创建一个README文件。这有时可能有用,但我们已经通过命令行创建了一个README,所以我不打算勾选那个按钮。

我们还可以添加一个.gitignore模板,我不打算这样做。我们稍后会讨论.gitignore的作用,但我现在不打算向存储库添加一个。我现在也不打算向存储库添加许可证。

但这些都是选择,当你更熟悉使用GitHub时,你可能希望做出与我在这里坚持默认值不同的选择。

好的,然后在底部,有一个绿色的按钮说“Create repository”。好的,GitHub很友好地为你提供了说明,告诉你如果你有一个现有的存储库要推送到这里该怎么做,或者如果你需要在命令行创建一个新的存储库。

你可能会注意到,如果你遵循命令行说明,它们看起来与我们创建存储库时已经做的非常相似。我们已经创建了存储库,所以我们将使用这些说明。

有两种方法可以做到这一点,一种是直接复制这个文本块并粘贴到你的命令行中。我将更慢地逐步讲解,因为我们在这里是为了学习。

为了从我们的计算机连接到GitHub,我们首先需要知道这个存储库的地址。获取它的简单方法是查看这个文本块。这看起来像一个网址,实际上是一个SSH地址。我们已经设置了SSH密钥,所以我们能够使用SSH地址。我只需复制这个。

随着你对GitHub更熟悉,你会认识到这种模式,你会更经常看到它,甚至可能学会记住它。但今天,与其尝试记住,我直接复制。

这是GitHub上存储库的地址。那么,我们需要做什么?我们需要告诉我们的本地存储库如何连接这个远程存储库。

Git有命令可以做到这一点。那个命令叫做git remote add

所以你可以在这个块下面看到我们将要输入的命令。我们需要添加一个新的远程,所以是git remote add origin,然后我们需要指定地址。我复制了那个地址,所以我要用Command+V粘贴它。好的,现在我要按回车。

现在我们可以实际查看我们当前在哪个分支上,通过输入git branch。我们在一个名为master的分支上,这是一个默认名称。我们更喜欢将默认名称改为main,现在这样做的方法是使用命令git branch -M main

然后,我们已经在本地提交了一些更改,现在我们希望将它们推送到GitHub。这意味着这些更改或提交在我们的本地存储库上,我们希望它们在GitHub存储库上。那就是推送,从本地到远程。你可以想象将这些更改从你身边推走,从近到远。

可能是git push。我们加上-u来指示我们正在跟踪哪个上游分支,叫做origin

确保……好的。好的,所以我做了一个推送,我已经将本地计算机上的提交从本地带到了远程。再次强调,这类似于推送某物,你把它从你身边推到更远的地方。这意味着我们在本地计算机上所做的提交现在应该反映在这个远程存储库中。

所以我刚刚重新加载了我的浏览器。我可以看到,事实上,我创建的README文件显示在GitHub版本的存储库中。GitHub对名为README的文件很智能,它知道它们通常是首先要看的地方,因此它把它们变成了存储库的一种主页。所以我们可以在这里看到,README的内容是“Hello GitHub!”。

我将在这里暂停一下,确保其他人都按照我输入的步骤操作,并且我们都达到了相同的点:你在你的GitHub账户中创建了一个存储库,并且你已经从你的本地存储库推送了一些提交到GitHub版本的存储库。

再次,当你成功时,请给出绿色的勾选标记。如果有人给出红色的X,我们有助手可以加入聊天,希望解决你的任何问题。

聊天中的人也是,是的,所以助手们,数据实验室的工作人员,如果你看到持续的问题并想向团队提出,请插话,我会尽力回答任何问题。

我认为这会很有帮助。有些人在终端收到消息,要求他们登录GitHub。我在想,为什么它要求我登录?我已经通过SSH密钥连接了。也许你可以谈谈那里发生了什么。

是的,好的,所以如果你收到一条消息说你需要登录,可能是因为你连接到了网址,抱歉,让我解释一下我刚才做了什么。我刚刚创建的存储库的GitHub主页上,文件列表顶部有一个绿色的按钮,写着“Code”。

如果我点击那个按钮,会得到一个下拉菜单。默认情况下,对我来说,这会显示存储库的SSH地址,我向你展示了我是如何连接的。

如果你没有设置SSH密钥,或者你只是碰巧复制了存储库的网址而不是SSH地址,你会复制类似这样的东西。这看起来像一个普通的网址,因为它正是如此,它完全匹配我地址框中的这个网址。

如果你以这种方式连接到GitHub存储库,GitHub每次你尝试与该存储库交互时都会要求你输入密码。我们不喜欢这样,拥有SSH密钥的全部意义在于你可以使用密钥解锁你的账户。所以我们不想还必须使用密码,我们不想一直使用密码。

我们将在这里,再次复制SSH地址。现在,这可能……好吧,所以我们现在必须做一些稍微高级的事情,那就是查看我们存储库的隐藏.git文件夹。

我仍然在命令行中查看相同的位置,它只是一个名为my_first_github_repo的本地文件夹。当然,如果我在这里使用ls命令列出文件,只有一个,叫做README。但如果我也用-a标志显示隐藏文件,我们看到有一个名为.git的文件夹,这就是Git知道这是一个存储库的方式。

我们可以查看该文件的内容,ls一下。这些大部分我们不想碰,但config我们可能需要。

Tyler喜欢Vim并在阅读器中使用它,我喜欢nano。但抱歉,nano不会为Windows用户安装,所以让我使用Vim来保持跨平台。如果我使用Vim打开.git/config,你会看到文件内容看起来像这样。

你可能在这里有类似[remote "origin"]的内容,其中URL是https://github.com/your_account/my_first_github_repo.git。那是网址版本。

我预计这就是人们遇到被要求登录的情况。

如果发生了这种情况,那么你可以在Vim中更改那个文本。我已经按i进入插入模式。我要删除这个地址?然后回到GitHub,点击“Code”,点击“SSH”,点击这个小按钮复制地址,回到我的命令行,然后粘贴。因为我在Vim中,我想通过按……保存这个文件。我在这里处于插入模式,我按Esc退出插入模式,然后输入冒号,这样Vim就知道我要输入命令了,你可以在左下角看到冒号,我输入wq。这意味着写入(保存)并退出。

完成之后,你应该就能够……嗯,你应该能够推送而不必输入密码。嗯,这样做了吗?好吧,我暂停一下,看看这是否为任何人解决了问题。

还有一种情况,取决于你的配置方式,每次推送时都可能要求你输入密码。我们通常不这样做,但如果你认为这对你很重要,这是另一种安全层。如果你遇到这种情况,我们很乐意在办公时间讨论。哦,是的,所以我想Pamela说的是,你计算机上的SSH密钥可能附加了密码,就像你在设置SSH密钥时,你可能被要求输入密码。

我总是默认不在我的SSH密钥上设置密码。

但是,如果在遵循设置SSH密钥的说明时,你添加了密码,那么那就是你需要输入的密码。在输入git push之后。这对人们来说可能是问题吗?

有可能,我认为这是一个很好的总结。如果个人遇到麻烦,只需在聊天中评论,我们会尽力帮助你。


在GitHub上直接编辑与拉取更改

希望暂停和讨论的时间足够长,让大多数人都达到了这一点:你在GitHub上创建了一个存储库,你在计算机上创建了一个匹配的存储库,你连接了它们,然后将你的提交从本地计算机推送到远程。

现在我想展示一个很酷的技巧,GitHub为我们做的一件好事。哎呀,我可能做得太快了。我们在这里查看我的README文件,你可以看到的一件事是,当你在GitHub中浏览文件时,实际上我只是点击README.md。所以当你在GitHub中点击一个文件时,你会得到一个看起来像这样的界面。

如果你想进行小的更改,你可以不经过拉取到本地计算机、进行更改、提交然后推回远程的过程,而直接进行。

方法是点击这个小铅笔图标,直接在GitHub内编辑这个文件。我只需添加几行新内容,让它看起来像是GitHub在回复我。GitHub会说“Hi Wesley!”。

好的,这就是我希望放入文件的更改。然后我滚动到这个页面的底部,我们这里有一个对话框,说“好的,这是你提交更改的方式。”我会说这个更改是“Add a reply from GitHub.”。我们知道是谁做的更改,我们将直接提交到主分支。然后提交更改。

好的。现在我们立即看到更改反映在存储库中。

接下来我要做的是,我刚刚对远程存储库做了一个更改,我想向你展示推送命令的反向操作,即将远程存储库上反映的更改获取到本地。当我们从本地到远程时,我们称之为推送。当我们从远程到本地时,我们称之为拉取。

所以我在这里,在我的Git存储库中。我可以直接输入git pull。远程存储库上的更改现在已经被拉取到我的本地存储库中,如果我检查README.md文件的内容,我可以看到这一点。所以我用less README.md。我们可以看到这是我从GitHub添加的内容。

所以我们开始看到如何可能将更改从一个存储库版本反映到另一个版本。你或许可以想象,当多人在同一个存储库中协作时,这将多么有用。


再次本地编辑并推送

所以现在我们需要……嗯,现在我们将开始做一些更改。我们将开始做一些更改,看看那如何反映在我们的存储库的远程版本中。

我们已经本地创建了一个文件,推送到远程,在远程做了一些更改并将它们拉取到本地,现在让我们最后再更改一次那个文件。

我将再次使用Vim编辑器,因为这是跨平台的,我想我可以用vi。好的,我要按i进入插入模式,在这个文件的底部添加:“Working on this created during Datalab workshop.”。

然后为了保存那个更改,我按Esc退出插入模式,按冒号开始输入命令,我想要的是保存并退出。在Vim中叫做写入并退出,所以w表示写入,q表示退出,然后我按回车。

所以我保存了文件,现在我们仍然可以访问所有传统的Git工具。例如,我们可以查看git status。我们看到README.md文件已被修改。

为了将这个更改添加到提交中,我将使用git add函数或命令。要添加更改的文件是README.md。现在我想提交这些更改,并附上提交消息:“Added a bit of context to the readme.”。

现在,再次,我们仍然拥有所有相同的传统Git工具,所以再次,git status。工作树是干净的。但是因为我们现在正在与远程存储库交互,Git很好心地告诉我们,我们领先远程存储库一个提交。也就是说,我刚刚在本地存储库上做的提交尚未反映在远程存储库中。

是的。几分钟前,在做git pull之前,我检查了这个,我会看到我们落后远程存储库一个提交,但我忘了做。

所以无论如何,在这一点上,我们领先origin一个最后的提交。为了修复这种情况,我将使用命令git push。即使在这种情况下没有必要,因为我们有如此简单的项目,我将指定我想推送到origin,我想推送的分支是main

好的。再次,我们得到了关于进行了多少更改、到哪个分支、我们从哪个提交开始、我们现在在哪个提交的有用消息。我们只是想确保所有这些更改实际上已经反映在这个存储库的远程版本中,事实上它们已经反映了。

所以,希望足够简单,大家都能跟上。我们只是……哦,是的。


使用.gitignore忽略文件

所以,在从处理在我们个人账户上创建并与之个人交互的存储库,转向协作处理我们所有人一起的单个存储库之前,我想回到我们第一次谈论GitHub时提到的一些事情,那就是如何处理你不想被远程跟踪的文件。

在GitHub中有一个特殊的文件叫做.gitignore来处理这个任务。我们现在没有,它只是一个简单的文本文件。

所以如果我列出这个存储库的内容,这里没有叫做.gitignore的东西。实际上,要看到.gitignore,我们必须用-a标志显示隐藏文件。但即使是隐藏文件也不包括任何叫做.gitignore的东西,所以让我们创建一个。touch .gitignore

然后当我列出文件ls -a时,我看到现在有一个文件叫做.gitignore。我还没有向其中添加任何东西。所以让我们进去向.gitignore添加一些东西。

所以我将再次使用Vim。.gitignore的工作方式是你简单地告诉它要忽略哪些文件,你是在告诉Git要忽略哪些文件。

你可以用一些快捷方式来做。例如,如果你输入一个星号,它匹配任何东西。所以,例如,如果我想忽略所有以.xls结尾的文件,所以如果我想忽略Excel文件,你可以放一个星号来匹配任何名称,然后是.xls

然后当我按Esc退出插入模式,按:wq写入并退出。然后我可以……嗯,在这一点上,基于那个.gitignore文件的内容,Git将默认忽略任何文件,任何Excel文件,任何以.xls结尾的文件。这对Excel文件来说不是超级有用,但对于像……所以我会回到.gitignorevi .gitignore

你们中的一些Mac用户可能会看到一堆名为.DS_Store的文件,这些是操作系统的一部分。我们真的不想跟踪它们,它们是Mac文件结构的一部分,与我们的项目无关。所以让我们告诉Git忽略它们。

也告诉Git忽略任何与OAuth密钥有关的东西,这些密钥帮助我们了解在与GitHub交互时我们被允许或不被允许做什么。所以我可以告诉Git忽略任何名为.httr-oauth的文件。

我可以告诉Git忽略整个名为data的文件夹,通过命令data/和星号,其中星号将匹配任何东西。你可能会注意到,这些文件实际上都不存在,这没关系。你不必告诉Git忽略真实的文件,你可以告诉Git忽略可能最终出现在你项目中的模式,当它们出现时,Git会知道忽略它们。如果这些不是真实的文件,如果这些现在不匹配任何东西,它不会引起任何问题。

我想快速插一句,对于基本的项目,你可能不需要修改你的.gitignore,但我们想确保你知道如何做,因为正如Wesley提到的,有些文件你不需要跟踪。例如,你的数据文件通常不应该在数据文件本身进行版本控制,你应该使用脚本来创建新的提取或清理版本等,然后你会跟踪你的脚本。所以,如果这还不清楚,请查看阅读器,有一些其他好的例子和叙述,当你到达那个点时,会引导你完成。

是的,所以对我们来说,这不是超级相关,但对于真实项目来说,知道是好的。好的,所以我用:wq命令退出这个文件。

现在,为了让这个成为项目的一部分,而不仅仅是存在于我计算机上的东西,我当然需要将它添加到跟踪中,提交更改,并将其推送到这个存储库的远程版本。所以我会用git add .gitignore将它添加到跟踪中。哎呀,我在这里犯了几个错误,我拼错了。嗯,只是点击。Git会说.gitignore不是一个Git命令,所以我忘了说git add。因为我想把它添加到跟踪中,然后当然我在文件名中打错了字。没问题,它很有帮助,它告诉你一些有用的消息。

我现在想提交那个更改。git commit -m "I created a .gitignore."。哇。现在我要推送。git push到远程存储库。

如果我现在切换回远程存储库并重新加载页面,我会看到是的,有一个.gitignore文件。

现在有很多其他有用的信息。你可以查看过去的提交,或者查看每个提交中所做的更改。你也可以……嗯,我可能必须……就在那里,我们看到……嗯。我们还看到了我们当前所在提交的密钥。这个时钟图标显示了存储库的历史,所以现在我们已经做了一堆提交,我们可以看到GitHub喜欢如何显示我们所有提交的列表。也可以通过点击这些按钮,查看每个过去提交时存储库的状态。

无论如何,这不是我们现在要做的。我们现在要做的是开始协作处理一个单一的存储库,所以这是GitHub用于协作开发的核心内容。


团队协作:克隆共享存储库与分支

我想让你们都访问的地址,我将把它输入我的浏览器,但希望助手之一会将其复制到聊天中。它是github.com/ucdavis-datalab/workshop_git_for_teams_sandbox。哎呀,这不是我想要的,我想去……抱歉,我们为每个人粘贴在聊天中了,所以我想学员们都在那里了。所以这是UC Davis的沙盒,是的,训练沙盒。所以我只是点击聊天中的链接,而不是试图凭记忆记住它,因为它有点复杂,有很多下划线。是的,UC Davis Datalab训练/workshop_git_for_teams_sandbox。所以我们都应该导航到这个页面。你可以

005:大语言模型基础 🧠

在本节课中,我们将学习大语言模型的基础知识,包括它们是什么、如何工作,以及如何开始在自己的研究中使用它们。我们将从核心概念入手,然后进行实际操作,学习如何加载模型、理解分词过程以及提取文本的嵌入表示。


概述

大语言模型是建立在海量文本数据上的大型神经网络模型。它们不仅仅是生成式聊天机器人,更是能够为文本生成密集向量表示(即嵌入)的强大工具,这些嵌入可以用于各种研究目的。本节课将引导你了解这些模型的基本原理和实际应用。


模型架构与训练

上一节我们概述了大语言模型的能力。本节中,我们来看看这些模型是如何构建和训练的。

大语言模型通常指任何基于Transformer架构的大型神经网络模型。这些模型在数十亿甚至数万亿的文本标记上进行训练,计算成本极高。因此,常见的模式是使用一个预训练模型(即在海量通用数据上训练好的模型),然后针对特定任务对其进行微调

训练目标通常是下一个标记预测(如GPT系列)或掩码语言建模(如BERT系列)。在掩码语言建模中,模型被训练来预测文本中被遮盖([MASK])的标记。


获取与加载模型

理解了模型的基本原理后,我们需要知道如何获取并使用它们。最常用的模型库是 Hugging Face

以下是访问和加载模型的步骤:

  1. 访问Hugging Face:前往 Hugging Face 模型库,你可以找到成千上万个模型。
  2. 选择模型:根据你的任务(如文本分类、特征提取、文本生成)进行筛选。我们将使用一个经典模型:Google的 BERT
  3. 加载模型:使用 transformers 库中的 AutoTokenizerAutoModel 类来加载模型和分词器。
from transformers import AutoTokenizer, AutoModel

model_name = "google-bert/bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModel.from_pretrained(model_name)

首次运行时会从网上下载模型文件,之后会存储在本地缓存中(通常在 ~/.cache/huggingface 目录下)。


理解分词:子词标记化

在将文本输入模型之前,必须进行分词。与传统NLP不同,大语言模型使用子词标记化

子词标记化不是简单按空格分割单词,而是将单词拆分成更小的、常见的子单元(子词)。例如,单词“tokenization”可能被拆分为 ["token", "##ization"]。这样做的好处是:

  • 处理罕见词:模型可以为未见过的单词组合子词来构建表示。
  • 压缩词汇表:无需为每个可能的单词保留一个独立的标记,大大减少了词汇表大小。
  • 跨语言能力:这种方法可以应用于任何字符序列,包括非拉丁文字。

分词器本身也是一个通过统计训练得到的模型。它从字符开始,逐步合并最常见的字符对,最终形成词汇表。

# 示例:查看分词结果
phrase = "large language models use subword tokenization"
tokens = tokenizer(phrase)
print(tokenizer.decode(tokens['input_ids'])) # 查看解码后的文本(可能包含特殊标记)
print(tokens['input_ids']) # 查看标记ID
# 可以遍历查看每个标记对应的子词
for token_id in tokens['input_ids']:
    print(tokenizer.decode(token_id))

你会看到除了子词外,还有 [CLS][SEP] 等特殊标记,它们用于表示序列的开始、结束或分隔不同句子。


生成文本嵌入

分词完成后,我们就可以将文本输入模型,获取其嵌入表示。嵌入是一个高维度的密集向量,代表了文本的语义信息。

BERT等编码器模型能生成上下文相关的动态嵌入。这意味着同一个单词(如“bank”)在不同语境(“river bank” vs “bank teller”)中会有不同的向量表示。

以下是获取嵌入的步骤:

# 1. 准备输入
sentence = "Then I tried to find some way of embracing my mother's ghost."
inputs = tokenizer(sentence, return_tensors="pt", padding=True, truncation=True) # 返回PyTorch张量

# 2. 将模型设置为评估模式(关闭Dropout等训练层)
model.eval()

# 3. 不计算梯度,进行前向传播以获取嵌入
import torch
with torch.no_grad():
    outputs = model(**inputs, output_hidden_states=True)

# 4. 提取嵌入
last_hidden_state = outputs.last_hidden_state # 最后隐藏层状态,形状为 (批大小, 序列长度, 隐藏层维度)
pooler_output = outputs.pooler_output # 通常对应 [CLS] 标记的池化输出,作为整个序列的摘要
all_hidden_states = outputs.hidden_states # 所有层的隐藏状态(元组)

print(f"序列长度: {last_hidden_state.shape[1]}")
print(f"每个标记的嵌入维度: {last_hidden_state.shape[2]}")
print(f"[CLS]标记的嵌入维度: {pooler_output.shape[1]}")

嵌入的使用策略

现在我们已经得到了多维的嵌入数据,接下来需要决定如何使用它们。以下是一些常见的策略:

  • 使用 [CLS] 标记:对于分类任务,许多模型设计为使用 [CLS] 标记的嵌入作为整个序列的表示。
  • 池化操作
    • 平均池化:取序列中所有标记嵌入的平均值。
    • 最大池化:取每个特征维度上所有标记嵌入的最大值。
  • 组合多层:研究表明,底层嵌入包含更多句法信息,高层嵌入包含更多语义信息。可以取最后几层的 [CLS] 嵌入进行平均、求和或拼接。

对于简单的句子级表示需求,推荐使用 sentence-transformers 库,它封装了这些池化策略。


微调模型用于特定任务

预训练模型很强大,但要让其在你的特定数据或任务上表现更好,需要进行微调。微调会更新模型的权重,使其适应新领域。

我们以使用BERT进行文本分类(图书简介分类)为例,展示微调的基本流程:

# 以下代码概述流程,具体实现请参考完整教程
from transformers import AutoModelForSequenceClassification, Trainer, TrainingArguments
from datasets import Dataset
import pandas as pd

# 1. 加载并准备数据
df = pd.read_parquet('book_blurbs.parquet')
# ... 数据预处理,创建标签映射 ...
dataset = Dataset.from_pandas(df[['text', 'label']])

# 2. 分词
def tokenize_function(examples):
    return tokenizer(examples['text'], padding=True, truncation=True)
tokenized_datasets = dataset.map(tokenize_function, batched=True)

# 3. 加载用于序列分类的模型(在BERT基础上添加了一个分类头)
num_labels = len(df['genre'].unique())
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=num_labels)

# 4. 定义训练参数
training_args = TrainingArguments(
    output_dir='./results',
    num_train_epochs=3,
    per_device_train_batch_size=32,
    per_device_eval_batch_size=64,
    warmup_steps=500,
    weight_decay=0.01,
    logging_dir='./logs',
    evaluation_strategy="epoch", # 每个epoch后评估
)

# 5. 创建Trainer并开始训练(此处仅示意,实际运行需要时间)
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_datasets['train'],
    eval_dataset=tokenized_datasets['test'],
    tokenizer=tokenizer,
)
# trainer.train() # 开始训练

训练完成后,你可以保存模型,并使用 pipeline 轻松进行推理:

from transformers import pipeline

classifier = pipeline("text-classification", model=my_fine_tuned_model, tokenizer=tokenizer)
result = classifier("A new text about science and discovery.")
print(result)

文本生成与解码策略

最后,我们简要看看如何用GPT等解码器模型进行文本生成。生成的核心是从模型预测的下一个标记的概率分布中进行采样。

from transformers import AutoModelForCausalLM, GenerationConfig

# 1. 加载生成模型(如GPT-2)
gen_model_name = "openai-community/gpt2"
gen_tokenizer = AutoTokenizer.from_pretrained(gen_model_name)
gen_model = AutoModelForCausalLM.from_pretrained(gen_model_name)

# 2. 准备输入
prompt = "It was the best of times, it was"
inputs = gen_tokenizer(prompt, return_tensors="pt")

# 3. 生成文本
# 使用贪婪解码(每次选择概率最高的标记)
greedy_output = gen_model.generate(**inputs, max_new_tokens=20, do_sample=False)
print("Greedy:", gen_tokenizer.decode(greedy_output[0], skip_special_tokens=True))

# 使用采样解码(引入随机性)
sampled_output = gen_model.generate(**inputs, max_new_tokens=20, do_sample=True, top_k=50)
print("Sampled:", gen_tokenizer.decode(sampled_output[0], skip_special_tokens=True))

常见的解码策略包括:

  • 贪婪搜索:始终选择概率最高的下一个标记。可能生成重复或单调的文本。
  • 束搜索:保留多个候选序列,最终选择整体概率最高的序列。生成质量更高,但速度慢。
  • Top-k 采样:仅从概率最高的k个候选中采样。
  • Top-p(核)采样:从累积概率超过p的最小候选集中采样。
  • 温度调节:调整softmax前的logits值。温度T高(>1)增加随机性,温度低(<1)使输出更确定。

你可以通过 GenerationConfig 来配置这些参数。

对于聊天或指令跟随任务,需要使用专门微调过的模型(如ChatGPT或指令微调模型),并按照特定的提示模板构建输入。


总结

本节课中我们一起学习了:

  1. 大语言模型基础:了解了Transformer架构、预训练和微调的概念。
  2. 实战准备:学会了从Hugging Face获取并加载模型。
  3. 核心处理:理解了子词标记化的原理及其重要性。
  4. 获取表示:掌握了如何从模型中提取文本的上下文嵌入向量。
  5. 应用策略:探讨了使用[CLS]标记、池化操作等策略来处理嵌入。
  6. 任务适配:概述了如何通过微调让模型适应文本分类等特定任务。
  7. 生成文本:初步了解了使用生成模型及不同解码策略进行文本生成。

大语言模型是强大的工具,但其本质是处理标记而非直接理解语言。理解其内部运作机制,能帮助你在学术研究中更有效、更批判性地使用它们。

006:构建本地大语言模型 🦙

在本节课中,我们将学习如何在本地计算机上安装和运行开源大语言模型,特别是使用 Ollama 框架。我们将从基础安装开始,逐步学习如何通过命令行和 Python 与模型交互,并探索文本向量化、相似度计算以及检索增强生成等核心概念。

概述

本教程旨在提供一个简单直接的入门指南,帮助初学者在自己的笔记本电脑上部署和操作大语言模型。我们将重点关注数据隐私、本地运行的优势以及如何通过编程方式利用模型进行文本分析。


安装与基础设置

上一节我们介绍了课程目标,本节中我们来看看如何安装 Ollama 并运行第一个模型。

首先,访问 Ollama 官方网站下载安装程序。

https://ollama.com/download

下载完成后,根据您的操作系统(Windows、macOS 或 Linux)运行安装程序。安装过程非常简单,只需按照提示操作即可。安装成功后,您会在系统任务栏(Windows/macOS)或系统托盘(Linux)看到一个羊驼图标,这表明 Ollama 服务已在后台运行。

接下来,我们需要通过命令行界面与 Ollama 交互。请打开您系统的终端(Terminal、Command Prompt 或 PowerShell)。

在终端中,输入以下命令来验证 Ollama 是否安装成功并查看可用命令:

ollama help

这个命令会列出所有可用的 Ollama 命令,例如 serverunpull 等。


下载并运行第一个模型

现在,我们将从云端拉取一个预训练的大语言模型到本地。我们将使用 Meta 发布的 Llama 3.2 模型。

在终端中输入以下命令:

ollama pull llama3.2

这个命令会从 Ollama 的模型库中下载 Llama 3.2 模型。下载时间取决于您的网络速度。下载完成后,您就可以在本地运行这个模型了。

使用以下命令启动模型并进入交互式聊天模式:

ollama run llama3.2

启动后,终端提示符会改变,您可以像使用 ChatGPT 一样直接输入问题。例如,输入“今天天气怎么样?”,模型会生成回答。

要退出交互模式,请输入 /bye 命令。


通过 Python 与模型交互

上一节我们通过命令行与模型对话,本节中我们来看看如何用 Python 程序化地调用模型,这为自动化处理打开了大门。

首先,确保您的 Python 环境已安装 ollama 库。在终端中运行:

pip install ollama

或者,如果您在 Jupyter Notebook 中,可以使用:

!pip install ollama

安装完成后,我们可以在 Python 脚本中与 Ollama 服务通信。以下是基本步骤的代码示例:

# 导入必要的库
from ollama import Client

# 创建客户端,连接到本地运行的 Ollama 服务
client = Client(host='http://localhost:11434')

# 指定要使用的模型
model = 'llama3.2'

# 定义要发送的提示词
prompt = "请用简单的话解释什么是人工智能。"

# 发送请求并获取响应
response = client.chat(model=model, messages=[
    {
        'role': 'user',
        'content': prompt,
    }
])

# 打印模型的回复
print(response['message']['content'])

这段代码创建了一个客户端,向本地 Ollama 服务发送一个提示,并打印出模型的响应。role: 'user' 表明消息来自用户。


理解模型核心:文本向量空间

在深入更复杂的应用之前,我们需要理解大语言模型的一个核心概念:文本向量空间。这并非生成式回答,而是模型“理解”文本的基础。

简单来说,模型中的每个词(或更准确地说,每个“词元”或 token)都被表示为一个高维空间中的向量(一组数字)。这个向量定义了该词在语义空间中的“位置”。

核心思想

  • 语义相似的词,其向量在空间中的位置也相近。
  • 通过计算向量之间的余弦相似度,我们可以量化两个词或两段文本的语义相似性。

我们可以用一个地理类比来理解:假设每个词是地球上的一个位置。它的向量就像是从多个不同观测站(例如,北极点、赤道某点、太空中的卫星)测量到该位置的距离集合。如果两个地点的所有距离测量值都相似,那么这两个地点在地理上就非常接近。

在代码中,我们可以使用专门的“嵌入模型”来获取文本的向量表示。首先,下载一个嵌入模型:

ollama pull mxbai-embed-large

然后,在 Python 中获取文本的向量:

from ollama import Client
import numpy as np

client = Client(host='http://localhost:11434')
embed_model = 'mxbai-embed-large'

# 获取一段文本的向量表示
text = "大语言模型很有趣。"
response = client.embed(model=embed_model, input=text)
text_vector = response['embeddings'][0] # 这是一个数值列表(向量)

print(f"文本向量的维度:{len(text_vector)}")
print(f"向量前10个值:{text_vector[:10]}")

这个 text_vector 就是文本“大语言模型很有趣。”在模型语义空间中的数学表示。


应用实践:文本分类与检索增强生成

上一节我们了解了文本向量的概念,本节中我们来看看如何利用它进行实际的文本分析,例如查找相似文档或构建一个智能检索系统。

1. 文本相似度计算与分类

假设我们有一个小型文档库,我们想找出与用户查询最相关的文档。以下是实现步骤:

from ollama import Client
import numpy as np
from numpy.linalg import norm

client = Client(host='http://localhost:11434')
embed_model = 'mxbai-embed-large'

# 假设的文档库
documents = [
    "羊驼是骆驼科的动物,原产于南美洲。",
    "Python是一种流行的编程语言,适用于数据科学和人工智能。",
    "巴黎是法国的首都,以埃菲尔铁塔和卢浮宫闻名。",
    "深度学习是机器学习的一个分支,使用神经网络模型。",
    "健康饮食包括多吃水果、蔬菜和全谷物。"
]

# 1. 为所有文档生成向量
doc_vectors = []
for doc in documents:
    response = client.embed(model=embed_model, input=doc)
    doc_vectors.append(response['embeddings'][0])

# 2. 为用户查询生成向量
user_query = "哪些动物和羊驼有亲缘关系?"
response = client.embed(model=embed_model, input=user_query)
query_vector = response['embeddings'][0]

# 3. 计算查询与每个文档的余弦相似度,并找出最相似的
def cosine_similarity(vec_a, vec_b):
    """计算两个向量的余弦相似度"""
    return np.dot(vec_a, vec_b) / (norm(vec_a) * norm(vec_b))

most_similar_index = -1
highest_similarity = -1 # 相似度范围是[-1, 1],-1为初始化值

for i, doc_vec in enumerate(doc_vectors):
    similarity = cosine_similarity(query_vector, doc_vec)
    if similarity > highest_similarity:
        highest_similarity = similarity
        most_similar_index = i

# 4. 输出结果
if most_similar_index >= 0:
    print(f"最相关的文档是(索引 {most_similar_index}):")
    print(documents[most_similar_index])
    print(f"相似度分数:{highest_similarity:.4f}")

这个程序模拟了一个简单的文档检索系统。它没有让模型“编造”答案,而是从我们提供的可靠文档库中找出语义上最匹配的内容。

2. 构建检索增强生成流水线

检索增强生成(RAG)结合了信息检索和文本生成。系统先从一个知识库中检索出相关文档片段,然后让大语言模型基于这些片段生成答案,从而提升答案的准确性和可靠性。

以下是 RAG 工作流的简化概念代码:

# 接续上面的代码,假设已有 doc_vectors 和 documents

def simple_rag(query, knowledge_base_docs, knowledge_base_vectors, top_k=1):
    """
    简单的RAG函数。
    query: 用户问题
    knowledge_base_docs: 知识库文档列表
    knowledge_base_vectors: 对应的向量列表
    top_k: 返回最相关的K个文档
    """
    # 1. 检索:获取查询的向量,并找到最相关的文档
    response = client.embed(model=embed_model, input=query)
    query_vector = response['embeddings'][0]

    similarities = []
    for i, doc_vec in enumerate(knowledge_base_vectors):
        sim = cosine_similarity(query_vector, doc_vec)
        similarities.append((sim, i))

    # 按相似度排序,取前 top_k 个
    similarities.sort(reverse=True, key=lambda x: x[0])
    top_indices = [idx for _, idx in similarities[:top_k]]
    retrieved_docs = [knowledge_base_docs[i] for i in top_indices]

    # 2. 增强生成:将检索到的文档作为上下文,与问题一起提交给生成模型
    context = "\n\n".join(retrieved_docs)
    enhanced_prompt = f"""请基于以下上下文信息回答问题。
    如果上下文包含答案,请主要依据上下文回答。
    如果上下文不包含答案,你可以根据已知知识回答。

    上下文:
    {context}

    问题:{query}

    答案:"""

    # 使用生成模型(如llama3.2)来回答
    gen_response = client.chat(model='llama3.2', messages=[
        {'role': 'user', 'content': enhanced_prompt}
    ])
    return gen_response['message']['content']

# 使用示例
answer = simple_rag("羊驼吃什么?", documents, doc_vectors, top_k=1)
print("RAG系统回答:", answer)

这个 simple_rag 函数展示了 RAG 的核心两步:检索相关证据,然后增强提示词,让生成模型基于证据作答。


总结

本节课中我们一起学习了在个人电脑上运行大语言模型的完整流程。

  1. 安装与运行:我们成功安装了 Ollama,并拉取、运行了 Llama 3.2 模型,实现了本地化的对话AI。
  2. 编程交互:我们学会了通过 Python ollama 库与本地模型进行程序化交互,为自动化任务奠定了基础。
  3. 核心概念:我们深入了解了文本向量空间这一核心思想,明白了模型如何将语义转化为可计算的数学形式。
  4. 实际应用:我们动手实践了文本相似度计算检索增强生成两个关键应用。通过计算余弦相似度,我们可以对文档进行分类、聚类或检索。通过构建简单的 RAG 流水线,我们能够创建更准确、基于可信知识源的问答系统。

最重要的是,所有这一切都在您的笔记本电脑上完成,确保了数据的完全隐私和安全。您可以将这些技术应用于研究数据分析、文档管理、智能客服等多种场景,而无需依赖外部云服务或担心数据泄露。希望本教程为您打开了本地大语言模型应用的大门。

007:概念、架构与首次登录 🚀

概述

在本节课中,我们将学习远程计算的基本概念,了解什么是服务器以及如何通过SSH协议安全地连接到远程服务器。我们还将学习如何创建和使用SSH密钥进行身份验证,并初步了解在共享服务器上应遵守的基本行为准则。


什么是服务器?💻

上一节我们介绍了课程概述,本节中我们来看看什么是服务器。

从广义上讲,服务器就是一台计算机。具体来说,服务器是一台提供服务的计算机。在远程计算的语境下,我们所说的服务器通常指计算服务器,而不是网页服务器。

计算服务器本质上是一台位于某处(通常在恒温的地下室)的计算机,它允许用户从自己的电脑连接上来,以运行那些因各种原因无法在自己电脑上运行的程序。这些原因可能包括:程序需要太多内存、处理的数据量过大、运行时间过长,或者程序会直接导致你的个人电脑崩溃。

因此,服务器的核心理念是:有一台始终开机的机器,你可以连接到它来完成繁重的工作。


服务器的共同特征

以下是计算服务器通常具备的共同特征:

  • 操作系统:绝大多数服务器运行某种版本的Unix操作系统,如今通常是Linux。这意味着它们主要通过命令行进行控制。
  • 共享资源:服务器是集中式资源,通常由多人同时使用。因此,你需要了解如何在服务器上与他人和平共处,避免干扰他人的工作。
  • 专业支持:与个人电脑不同,高性能计算机通常有专门的支持人员(系统管理员)负责安装、维护、解决问题。
  • 高性能:服务器的计算和存储能力通常远强于个人电脑,能更快地处理任务。

如何连接到远程服务器?🔗

上一节我们了解了服务器的概念,本节中我们来看看如何建立连接。

你需要一种方式将你的本地计算机连接到那台位于“地下室”的远程计算机。和当今大多数通信一样,这种连接通过互联网进行。

连接远程计算机需要使用一种协议。协议是计算机之间相互通信的语言。具体到远程连接,几乎在所有情况下,你都会使用SSH协议

SSH代表“安全外壳”(Secure Shell)。其中,“外壳”(Shell)是你与计算机对话的方式(例如命令行界面)。“安全”意味着你从本地计算机发送到远程计算机的所有数据,以及远程计算机返回的数据都是加密的。只有你和远程计算机能读取这些数据,其他人无法拦截或窥探你的连接。

为了通过SSH连接到服务器,服务器需要知道你是谁。换句话说,你需要在服务器上拥有一个账户。


账户与身份验证:SSH密钥 🔑

上一节我们介绍了连接的基础——SSH协议,本节中我们来看看身份验证的具体方式。

服务器账户在概念上与你在线服务(如社交媒体)的账户类似:你有一个用户名和某种凭证(密码或密钥)来向服务器证明“真的是我”。

在UC Davis,高性能计算(HPC)系统不使用密码进行SSH身份验证,而是使用更安全的SSH密钥。因此,我们首先需要创建一对SSH密钥。

SSH密钥是一对非常长的字符串(一个公钥,一个私钥)。公钥可以自由分享,而私钥必须严格保密。服务器通过验证你是否拥有与公钥配对的私钥,来安全地确认你的身份。

以下是创建SSH密钥的步骤:

  1. 在本地计算机的终端中,输入以下命令:
    ssh-keygen
    
  2. 当提示“Enter file in which to save the key”时,建议为密钥指定一个描述性的名称(例如 hippo),而不是使用默认名称。这有助于管理多个密钥。命令格式参考如下:
    /home/your_username/.ssh/hippo
    
  3. 接下来会提示输入密码短语(passphrase)。这是一个可选的额外安全层,用于加密你本地计算机上的私钥文件。即使不设置,直接按回车也可以,但出于安全考虑,建议设置一个。
  4. 密钥生成后,你会在 ~/.ssh/ 目录下看到两个新文件:hippo(私钥)和 hippo.pub(公钥)。

核心概念ssh-keygen 命令用于生成SSH密钥对。私钥(如 hippo)必须绝对保密,公钥(如 hippo.pub)可以安全地分享给服务器。


在UC Davis HPC系统注册账户

拥有SSH公钥后,你就可以在UC Davis的HPC系统(如Hive集群)上注册账户了。

  1. 访问HPC账户注册页面(例如:HIPO系统)。
  2. 选择你要注册的集群(例如:Hive)。
  3. 填写注册信息,包括你的姓名、赞助人/小组等。
  4. 在指定字段中,粘贴你刚才生成的公钥hippo.pub文件的内容)。切记不要粘贴私钥
  5. 提交请求。账户创建需要系统管理员手动审批,这可能需要一些时间。

首次登录服务器

账户获批后,你就可以使用SSH命令登录服务器了。基本命令格式如下:

ssh your_username@server_address

例如,登录到Hive集群:

ssh oliver@hive.hpc.ucdavis.edu

如果是第一次连接某台服务器,SSH会显示该服务器的指纹并询问你是否信任。在确认服务器身份无误后,输入 yes 继续。

如果您的私钥设置了密码短语,此时会提示您输入。输入后(屏幕上无回显),即可成功登录。登录后,命令行提示符会发生变化,表示你现在操作的是远程服务器。

你可以像操作本地终端一样运行命令,例如 ls, pwd, date

要断开连接,只需输入 exit 命令。


配置SSH以简化登录

为了简化登录过程,避免每次输入用户名和服务器地址,可以配置SSH客户端。

编辑本地计算机上的SSH配置文件 ~/.ssh/config(如果不存在则创建):

nano ~/.ssh/config

添加以下内容(根据你的信息修改):

Host hive
    HostName hive.hpc.ucdavis.edu
    User your_username
    IdentityFile ~/.ssh/hippo

保存并退出后,你就可以使用简短命令登录了:

ssh hive

服务器行为准则与资源共享 🤝

上一节我们成功连接了服务器,本节中我们来看看在共享环境中应如何行事。

服务器是强大的共享资源。当你登录时,通常是连接到“登录节点”(或头节点)。实际的繁重计算任务应在后端的“计算节点”上通过作业调度系统(如Slurm,我们将在后续课程中学习)提交,而不是直接在登录节点上运行长时间或高资源消耗的任务,否则会影响其他用户并可能被管理员终止任务。

服务器上的主要资源包括:

  1. 计算带宽(CPU):多个用户共享时,每个人的任务会变慢,但通常是按比例公平分配的。
  2. 内存(RAM):内存耗尽会导致系统性能急剧下降(使用虚拟内存,速度极慢),因此需要谨慎管理。
  3. 存储空间(硬盘):空间是有限的,用尽后无法写入新数据。每个用户通常有配额限制。
  4. 存储带宽(I/O):多人同时读写大文件会降低每个人的速度,但也是按比例影响的。

用户隐私:尊重他人隐私至关重要。不要查看、修改或删除其他用户的文件和目录,即使权限设置错误允许你这样做。

与管理员互动:系统管理员是真实的人,他们工作繁忙。在寻求帮助前,请先阅读服务器文档并尝试自行解决问题。清晰地描述问题和你已尝试的步骤。对于重要任务,请提前规划,留出解决问题的时间。


在服务器与本地之间传输文件 📁

要在服务器上运行任务,经常需要将数据或代码上传到服务器,或者将结果下载到本地。以下是几种常用方法:

1. 从互联网直接下载到服务器

如果数据源在互联网上,可以直接在服务器上使用命令行工具下载,避免“先下到本地,再上传到服务器”的冗余步骤。

  • 使用 wget
    wget https://example.com/data/file.zip
    
  • 使用 curl
    curl -O https://example.com/data/file.zip
    
    -O 选项表示使用远程文件原名保存。

2. 在本地和服务器之间安全拷贝文件

SSH套件提供了安全的文件传输命令。

  • 使用 scp(安全拷贝)

    • 上传到服务器
      scp local_file.txt your_username@hive.hpc.ucdavis.edu:~/remote_directory/
      
    • 从服务器下载
      scp your_username@hive.hpc.ucdavis.edu:~/remote_file.txt ./
      
    • 在服务器间拷贝(不常用):
      scp user1@server1:~/file.txt user2@server2:~/ 
      
  • 使用 sftp(安全文件传输协议)
    sftp 提供了一个交互式会话,类似于一个专门用于文件管理的命令行环境。

    sftp your_username@hive.hpc.ucdavis.edu
    

    登录后,可以使用类似FTP的命令:

    • ls, lls: 列出远程/本地文件。
    • cd, lcd: 切换远程/本地目录。
    • put local_file: 上传文件。
    • get remote_file: 下载文件。
    • mkdir, rmdir, rm 等管理远程文件。
      输入 exitquit 退出sftp会话。

图形化工具:你也可以使用FileZilla等图形化SFTP客户端,通过拖放操作传输文件,其底层原理与sftp相同。


总结

本节课中我们一起学习了远程计算的基础知识。我们了解了服务器的概念和架构,学会了如何生成SSH密钥并在UC Davis HPC系统上注册账户。我们掌握了使用SSH命令登录和退出服务器的方法,并通过配置简化了登录流程。我们还讨论了在共享服务器上必须遵守的行为准则,包括资源管理和用户隐私。最后,我们学习了使用 wget/curlscpsftp 在互联网、本地计算机和服务器之间传输文件的基本技能。这些是开始使用高性能计算环境的第一步。

008:软件环境管理与Pixie入门 🚀

在本节课中,我们将学习如何管理计算环境中的软件,特别是如何在服务器上安装和管理不同版本的软件。我们将重点介绍一个名为Pixie的环境管理器,它可以帮助我们创建独立的虚拟环境,从而解决不同项目间软件版本冲突的问题。


理解软件环境管理的重要性

上一节我们介绍了远程计算的基本概念,本节中我们来看看为什么需要管理软件环境。

管理计算环境意味着管理你的硬件和软件。对于大多数人来说,频繁更换硬件并不现实,因此我们将重点放在软件管理上。软件管理之所以重要,有以下几个原因:

  • 软件缺失:你登录服务器后,可能发现需要的程序没有安装,你需要知道如何自行安装。
  • 版本冲突:不同的项目可能需要不同版本的软件。例如,一个旧代码库可能依赖Python 2,而你的新项目需要使用Python 3。
  • 可重复性研究:为了确保他人能够复现你的研究成果,必须明确记录所使用的软件及其版本。
  • 问题诊断:当遇到程序错误时,检查软件版本通常是排查问题的第一步。

为了有效管理软件,我们需要使用包管理器


什么是包管理器与环境管理器?

包管理器是用于安装、移除和更新软件的工具。例如,R语言中的 install.packages、Python中的 pip 或手机上的应用商店都是包管理器。

然而,并非所有包管理器都能创建虚拟环境。虚拟环境可以让你为不同的项目安装相互独立、甚至互不兼容的软件集合。例如,一个环境使用Python 2,另一个环境使用Python 3。

我们将要学习的Pixie,就是一个既能管理包又能创建虚拟环境的环境管理器


安装Pixie环境管理器

我们将使用Pixie,因为它跨平台(支持Windows、Mac、Linux),速度快,并且可以安装来自Conda Forge和PyPI的软件包。

首先,我们需要登录到服务器。请注意终端提示符的区别,这能帮助你区分是在本地电脑还是在服务器上操作。

  • 本地终端提示符示例username@localhost:~$
  • 服务器终端提示符示例username@login-01:~$

确认登录服务器后,按照Pixie官网的Linux安装说明执行安装命令。安装完成后,需要退出并重新登录服务器,或者重新加载shell配置,以使Pixie命令生效。

你可以通过运行以下命令来验证Pixie是否安装成功:

pixi --version

配置Shell以正确使用Pixie

如果Pixie命令未找到,可能是因为shell不知道去哪里寻找它。我们需要配置shell的 PATH 变量。

PATH 变量是一个由冒号分隔的目录列表,shell会按照顺序在这些目录中查找可执行命令。我们需要将Pixie的安装目录添加到 PATH 中。

以下是配置步骤:

  1. 编辑 ~/.bash_profile 文件,确保每次登录时都会加载其他配置文件。
  2. 编辑 ~/.profile 文件,在其中添加一行,将Pixie的 bin 目录路径添加到 PATH 变量的最前面。请务必将路径中的 your_username 替换为你自己的用户名
    export PATH="/home/your_username/.pixi/bin:$PATH"
    
  3. 保存文件,退出并重新登录服务器。

完成这些步骤后,再次运行 pixi --version 应该就能看到版本号了。


了解服务器预装软件(模块系统)

服务器管理员已经预装了一些常用的软件集合,它们被称为模块。你可以使用以下命令来查看和管理这些模块:

  • module avail:查看所有可用模块。
  • module load <模块名>:加载并使用某个模块(例如 module load python)。
  • module list:查看当前已加载的模块。
  • module unload <模块名>:卸载某个模块。

模块系统使用方便,但更新可能不够及时,且灵活性有限。对于更定制化的需求,我们使用Pixie。


使用Pixie创建和管理项目环境

现在,让我们开始使用Pixie的核心功能。Pixie围绕“项目”来组织环境。最佳实践是为每个项目创建一个独立的目录。

初始化Pixie项目

使用以下命令创建一个新的项目目录并初始化Pixie环境:

pixi init my_first_pixi_project
cd my_first_pixi_project

初始化后,目录中会生成一个 pixi.toml 文件。这个文件记录了项目的元信息(如名称、作者)和软件依赖列表,是项目环境的核心描述文件。

安装软件包

在项目目录中,使用 pixi add 命令安装软件包。Pixie会自动处理依赖关系。

pixi add python  # 安装Python(默认最新版本)
pixi add r       # 安装R语言

安装时,Pixie会使用“语义化版本约束”(例如 python >=3.13.5, <3.14),这既能保证获取安全更新,又能防止不兼容的大版本升级。

运行项目中的软件

要运行安装在当前Pixie环境中的命令,需要使用 pixi run 前缀:

pixi run python
pixi run R

更新与升级软件包

  • pixi update:安全地更新所有包,但遵守 pixi.toml 中的版本约束。
  • pixi upgrade:升级所有包,忽略版本约束,可能引入不兼容更改,需谨慎使用。

理解Pixie项目文件

在项目目录中执行 ls -la,你会看到几个关键文件:

  • pixi.toml:主要的项目和环境描述文件(应纳入版本控制)。
  • pixi.lock:精确锁定所有依赖包版本的文件(应纳入版本控制)。
  • .pixi/ 目录:实际存储软件包的目录(不应纳入版本控制,可删除以节省空间,之后可通过 pixi install 重建)。

全局安装工具

有些工具(如代码搜索工具 ripgrep)你希望在任何地方都能使用,而不是绑定到特定项目。可以使用全局安装:

pixi global install ripgrep

安装后,你就可以直接在命令行使用 rg 命令了。


使用Shell别名提升效率

为了减少输入长命令的麻烦,可以在 ~/.bashrc 文件中设置别名

例如,为 pixi run 创建一个简短别名:

alias pxr='pixi run'

保存文件并重新登录后,你就可以用 pxr python 来代替 pixi run python 了。

你还可以为常用命令添加默认选项,例如让 df 命令总是以人类可读的格式输出:

alias df='df -h'


课程总结

本节课中我们一起学习了如何在高性能计算环境中管理软件。

  1. 核心概念:我们理解了软件环境管理对于项目隔离、版本控制和科研可重复性的重要性。
  2. 工具引入:我们介绍了Pixie这一强大的环境管理器,它能够创建独立的虚拟环境并管理软件包。
  3. 实践操作:我们成功在服务器上安装了Pixie,并学会了如何创建项目、使用 pixi.toml 文件、安装/运行/更新软件包。
  4. 效率技巧:我们了解了如何配置Shell的 PATH 和使用别名来让命令行操作更加流畅高效。

通过掌握这些技能,你将能够为不同的计算项目构建稳定、可复现的软件环境,无论是在本地还是远程服务器上。

009:使用Miniconda打造个性化计算空间

概述

在本节课中,我们将学习如何创建可执行的脚本,以便将一系列命令行指令自动化。我们还将学习如何使用终端复用器 tmux 来管理远程计算会话,确保程序在断开连接后仍能持续运行。最后,我们将探讨如何监控进程的资源使用情况(如CPU、内存和磁盘空间),这对于在高性能计算集群上高效运行任务至关重要。


创建可执行脚本

到目前为止,我们一直在命令行进行交互式操作。但当你需要运行一个完整的分析,尤其是希望其他人能复现你的步骤时,将所有指令写入一个脚本会非常有帮助。脚本就像一份食谱,每次使用相同的工具运行相同的脚本,都应该得到相同的结果。

脚本本质上是一系列预先录制的指令,你可以一次性执行它们。无论是Python脚本、R脚本还是Shell脚本,这一点都成立。本节我们将主要使用Shell脚本。

脚本的运行方式

大多数脚本通过一个解释器在命令行运行。解释器是知道如何处理你提供的命令的程序。例如,Python和R是解释器,Shell程序(如bash)本身也是一个解释器。

运行脚本的常见模式如下:

解释器 脚本名 参数

例如,运行一个名为 script.py 的Python脚本:

python script.py input.txt output.txt

然而,还有另一种更简洁的方法:创建可执行脚本,这样你就不必在每次运行时都先指定解释器。调用方式如下:

./script.py input.txt output.txt

这里的 ./ 表示在当前目录下查找文件。之所以需要这样做,是因为Shell默认只在系统路径中查找命令,而新创建的脚本通常不在路径中。

Shebang:指定解释器

为了让Shell知道如何处理一个未指定解释器的可执行脚本,我们需要在脚本的第一行添加一个特殊的行,称为 shebang(或 hashbang)。它由 #! 开头,后面紧跟解释器的路径。

Shebang的格式

#!解释器路径 [可选参数]

例如,指定使用bash解释器:

#!/usr/bin/env bash

使用 #!/usr/bin/env bash 是一种更灵活的方式,它会在当前环境变量 PATH 中查找 bash,这在你使用Pixi等工具管理环境时尤其重要,可以确保使用正确版本的bash。

实战:创建一个简单的Bash脚本

接下来,我们将创建一个名为 beacon.sh 的简单bash脚本。该脚本接受一个名字作为参数,然后每秒打印一次问候语。

  1. 创建脚本文件
    nano beacon.sh
    

  1. 编写脚本内容

    #!/usr/bin/env bash
    
    name=$1
    while true
    do
        echo "Hello $name"
        sleep 1
    done
    
    • #!/usr/bin/env bash:shebang行,指定使用bash。
    • name=$1:将命令行的第一个参数赋值给变量 name。注意,bash中变量赋值时等号两边不能有空格。
    • while true:一个无限循环。
    • echo "Hello $name":打印问候语。使用变量时需加 $ 符号。
    • sleep 1:暂停1秒。
    • done:标记while循环结束。
  2. 保存并退出nano
    Ctrl+O 保存,按 Ctrl+X 退出。

  1. 赋予脚本执行权限
    创建的文件默认没有执行权限。我们需要使用 chmod 命令修改文件权限。

    chmod u+x beacon.sh
    
    • chmod:修改文件模式(权限)。
    • u+x:为文件所有者(user)添加执行(execute)权限。
  2. 运行脚本

    ./beacon.sh Wes
    

    脚本将开始每秒打印“Hello Wes”。要停止脚本,请按 Ctrl+C


使用Tmux管理持久会话

在远程高性能计算(HPC)环境中,我们经常需要运行耗时很长的任务。如果直接关闭终端窗口,正在运行的程序也会被终止。为了解决这个问题,我们需要一种方法来创建持久化的会话,即使断开连接,程序也能继续运行。

什么是Tmux?

tmux(终端复用器)是一个强大的工具,它允许你在一个终端窗口中创建多个会话窗口窗格。最重要的是,你可以从tmux会话中分离(detach),而会话及其内部运行的程序会在后台继续运行。之后你可以随时附加(attach)回该会话。

Tmux基础操作

  1. 启动一个新的tmux会话
    tmux
    
    或创建一个命名会话:
    tmux new-session -s beacon
    

  1. 在tmux会话中工作
    此时,你处于tmux会话内部,可以像在普通终端一样运行任何命令(例如,运行 ./beacon.sh)。

  1. 分离当前会话
    要离开tmux会话但让其继续运行,需要先进入tmux的命令模式。默认的触发键是 Ctrl+B,然后按 d
    • 操作:先按 Ctrl+B,松开后再按 d
      现在你回到了启动tmux之前的原始shell中,但 beacon.sh 仍在后台运行。

  1. 列出所有会话

    tmux list-sessions
    
  2. 重新附加到会话

    tmux attach -t beacon  # -t 代表目标(target),后接会话名
    

  1. 在会话内终止程序
    在tmux会话内,按 Ctrl+C 可以终止当前正在前台运行的程序(如我们的beacon脚本)。

  2. 结束一个tmux会话
    在tmux会话内,直接输入 exit 或按 Ctrl+D,会结束该会话及其中的所有进程。

Tmux窗格管理

除了会话,tmux还可以在同一个窗口内分割出多个窗格,方便同时查看代码和运行结果。

  • 垂直分割:在tmux命令模式下(Ctrl+B),输入 :split-window -v
  • 水平分割:在tmux命令模式下,输入 :split-window -h
  • 在窗格间导航:在tmux命令模式下(Ctrl+B),使用方向键(上、下、左、右)在窗格间切换。
  • 关闭当前窗格:在tmux命令模式下(Ctrl+B),按 x,然后确认。


监控进程与资源使用

为了在高性能计算集群上高效运行任务,我们需要了解程序使用了多少资源(CPU、内存、时间、磁盘空间)。这有助于我们在提交作业时,提出准确合理的资源请求。

查看进程:ps 命令

ps 命令用于显示当前进程的快照。

  • 查看当前终端内的进程

    ps
    

    输出列包括:

    • PID:进程ID,操作系统分配的唯一标识符。
    • TTY:进程关联的终端。
    • TIME:进程使用的CPU时间。
    • CMD:启动进程的命令。
  • 查看用户的所有进程

    ps -u username
    

  • 查看更详细的进程树关系
    ps -fj -u username
    
    这会显示父进程ID(PPID),有助于理解进程间的层级关系。

终止进程:kill 命令

如果一个进程无响应,可以使用其PID来终止它。

kill PID号

例如,根据 ps 命令找到beacon.sh的PID,然后执行 kill 12345

动态监控资源:top 命令

top 命令提供一个动态更新的视图,显示系统中进程的资源使用情况。

  • 启动top

    top
    
  • 仅查看当前用户的进程

    top -u username
    

  • 在top中切换内存显示单位
    • E(大写)循环切换顶部汇总信息的内存单位(KB, MB, GB...)。
    • e(小写)循环切换进程列表中的内存单位。

  • 退出top
    q

监控GPU资源

如果程序使用GPU,可以使用 nvidia-smi 命令来监控GPU使用情况和内存。

nvidia-smi

注意:此命令需要在有GPU的节点上运行,登录节点通常没有GPU。

检查磁盘使用情况

  • 查看目录磁盘使用du(disk usage)
    du -h --max-depth=1
    
    • -h:人类可读的格式(如K,M,G)。
    • --max-depth=1:只显示指定目录下一级子目录的大小。

  • 查看磁盘可用空间df(disk free)
    df -h .
    
    • -h:人类可读的格式。
    • .:查看当前目录所在文件系统的使用情况。


估算代码资源需求与时间复杂度

为了合理申请HPC资源,我们需要估算代码的运行时间和内存消耗。一个有效的方法是进行基准测试

使用 time 命令测量运行时间

time 命令可以测量一个命令或脚本执行所需的时间。

time ./nested.sh 100

输出示例:

real    0m0.101s
user    0m0.060s
sys     0m0.032s
  • real:实际流逝的“墙钟”时间。
  • user:进程在用户态消耗的CPU时间。
  • sys:进程在内核态消耗的CPU时间。

理解时间复杂度(Big O Notation)

代码的性能通常与输入数据的大小(n)相关,这种关系用大O符号描述:

  • O(1):常数时间,运行时间不随n增长。
  • O(n):线性时间,运行时间与n成正比。
  • O(n²):平方时间,运行时间与n的平方成正比(例如,双重嵌套循环)。

实践:通过基准测试推断

我们可以运行不同规模的小型测试,来推断处理大规模数据所需的资源。

  1. 创建测试脚本 nested.sh(一个双重循环):
    #!/usr/bin/env bash
    for ((i=1; i<=$1; i++))
    do
        for ((j=1; j<=$1; j++))
        do
            : # 空操作,仅消耗循环开销
        done
    done
    

  1. 运行并计时

    time ./nested.sh 100
    time ./nested.sh 200
    
  2. 分析结果

    • 如果输入从100变为200(翻倍),而运行时间变为原来的4倍,那么代码很可能是 O(n²) 复杂度。
    • 利用这个比例关系,你就可以估算出处理更大规模数据(如n=10000)所需的时间,从而为HPC作业申请合适的运行时间。

将输出重定向到文件
如果程序输出大量内容到屏幕,可以将其重定向到文件,避免干扰且便于保存。

time ./nested.sh 1000 > output.txt 2>&1
  • >:将标准输出重定向到文件。
  • 2>&1:将标准错误也重定向到标准输出(即同一个文件)。

总结

本节课我们一起学习了三个核心技能:

  1. 创建可执行脚本:我们学会了如何编写Shell脚本,添加shebang行,并使用 chmod 赋予执行权限,从而将重复的命令行操作自动化。
  2. 使用Tmux管理持久会话:我们掌握了如何使用 tmux 创建、分离、重连和终止会话。这是确保远程计算任务在断开连接后仍能持续运行的关键工具。
  3. 监控与估算资源:我们了解了如何使用 pstopnvidia-smidudf 等命令监控进程和系统资源。同时,通过 time 命令和基准测试,我们学习了如何估算代码的时间复杂度和资源需求,为在高性能计算集群上提交作业做好准备。

掌握这些技能,你将能够更自信、更高效地在远程计算环境中开展工作。

010:SLURM实战与服务器最佳实践 🚀

在本节课中,我们将学习如何在高性能计算(HPC)集群上实际运行计算任务。我们将从理解并行计算的基本概念开始,然后学习如何使用SLURM作业调度系统来提交和管理作业,包括交互式作业、批处理作业以及并行作业。

概述:并行计算与HPC集群

在开始之前,我们需要明确一些核心概念。到目前为止,您所进行的大多数计算可能都是顺序计算,即代码一行接一行地执行。然而,HPC的强大之处在于并行计算,即多个计算步骤可以同时运行。

并行计算主要有两种模式:

  • 多线程:一个程序启动多个线程,这些线程共享同一块内存空间。这通常在一台计算机(一个节点)内进行。
  • 分布式计算:多个独立的进程运行在不同的计算机(节点)上,它们通过文件或网络协议进行通信。这是更复杂但能处理更大规模问题的并行方式。

HPC集群由许多通过高速网络连接的计算机组成,每台计算机称为一个节点。我们登录的节点称为头节点,而实际执行计算任务的节点称为计算节点。为了公平、高效地管理所有用户对计算资源的使用,我们使用作业调度器。在本课程中,我们将使用名为SLURM的作业调度器。

1. 探索集群资源与SLURM基础

在提交作业之前,我们先了解一下如何查看集群的可用资源。

查看分区与账户信息

SLURM将计算资源组织成不同的分区,您可以将其理解为不同的任务队列。使用以下命令查看所有可用分区:

sinfo --summarize

该命令会显示分区的名称(如 highlow)、状态、时间限制和节点信息。

要查看您的账户具体可以访问哪些分区,请使用:

sacctmgr show associations user=$USER

查看作业状态

您可以使用 squeue 命令查看当前正在运行或排队的作业。添加 --me 参数可以只查看您自己的作业。

squeue --me

squeue 的输出包含了作业ID、分区、状态(R表示运行中,PD表示排队中)、运行时间、使用的节点和CPU数量等信息。

2. 运行交互式作业

交互式作业允许您像在头节点上一样,直接登录到一个计算节点并运行命令,非常适合调试和探索性工作。

提交交互式作业

使用 srun 命令并指定所需资源来启动一个交互式作业。以下是关键参数:

  • --time:请求的运行时间(例如 20 表示20分钟)。
  • --partition:指定分区(例如 low)。
  • --mem:请求的内存(例如 1G 表示1GB)。
  • --job-name:为作业命名(不能包含空格)。
  • --pty:指定启动一个伪终端,并运行一个shell(如 /bin/bash)。

运行以下命令示例:

srun --time=20 --partition=low --mem=1G --job-name=my_first_job --pty /bin/bash -l

命令执行后,如果资源可用,您的终端提示符会从 @login 变为 @hivecXXX(某个计算节点)。现在您可以在这个计算节点上运行任何命令了。

结束交互式作业

要结束交互式会话并返回头节点,只需输入 exit

在交互式作业内部,您可以通过环境变量 $SLURM_JOB_ID 获取当前作业的ID。

3. 运行非交互式(批处理)作业

对于需要长时间运行且无需人工干预的任务,我们使用非交互式作业。我们将创建一个包含SLURM指令和实际运行命令的批处理脚本,然后提交它。

创建批处理脚本

使用文本编辑器(如 nano)创建一个脚本文件,例如 hello.sh

#!/bin/bash
#SBATCH --time=20
#SBATCH --partition=high
#SBATCH --mem=1G
#SBATCH --job-name=myjob
#SBATCH --mail-user=your-email@ucdavis.edu
#SBATCH --mail-type=ALL
#SBATCH --output=myjob_%j.out

srun hostname

脚本解析:

  1. #!/bin/bash:指定脚本由Bash shell执行。
  2. #SBATCH 开头的行:这些是给SLURM的指令,定义了作业的资源需求和其他设置。
    • --output:指定标准输出日志的文件名,%j 会被替换为作业ID。
    • --mail-*:设置作业完成或失败时接收邮件通知。
  3. srun hostname:这是作业要执行的实际命令。使用 srun 来运行命令可以让SLURM更好地记录和管理作业步骤。

提交与监控批处理作业

使用 sbatch 命令提交脚本:

sbatch hello.sh

提交后,您会看到类似 Submitted batch job 1234567 的信息。您可以使用 squeue --me 查看作业状态。

作业完成后,输出日志会保存在您指定的文件(如 myjob_1234567.out)中。如果作业出错或您想提前终止它,可以使用 scancel 命令:

scancel 1234567

在作业中使用软件环境(如Pixi)

如果您的任务需要特定的软件环境(例如通过Pixi管理),只需在 srun 命令前加上 pixi run 即可。例如,要运行一个Python脚本:

#!/bin/bash
#SBATCH ...
srun pixi run python my_script.py

4. 运行并行作业

上一节我们介绍了如何运行单个任务,本节中我们来看看如何利用SLURM运行并行任务。SLURM主要支持两种并行模式:并行步骤数组作业

并行步骤

这种方法适用于需要同时运行多个不同命令或步骤的场景。我们在一个批处理脚本中编写多个 srun 命令,并让它们同时启动。

以下是关键技巧:

  1. 在每个 srun 命令末尾添加 & 符号,表示不等待该命令结束就继续执行脚本。
  2. 使用 wait 命令确保所有后台任务完成后,脚本才结束。
  3. 通过 #SBATCH --nodes 指定需要的节点总数,并在每个 srun 命令中用 --nodes=1 指定该步骤需要的节点数。

示例脚本 parallel_steps.sh

#!/bin/bash
#SBATCH --time=10
#SBATCH --partition=high
#SBATCH --mem=1G
#SBATCH --job-name=parallel_demo
#SBATCH --nodes=2

srun --nodes=1 hostname &
srun --nodes=1 hostname &

wait

提交此脚本后,两个 hostname 命令会并行执行,可能在不同的计算节点上。

数组作业

数组作业非常适合令人尴尬的并行问题,即需要将相同的任务运行很多次,且每次运行之间不需要通信。例如,用不同的参数处理100个数据文件。

您只需编写处理单个任务的脚本,SLURM会自动为您创建多个副本(一个数组)来运行。

关键参数:

  • #SBATCH --array:定义数组索引范围,例如 1-3 会创建3个独立的作业任务。

示例脚本 array_job.sh

#!/bin/bash
#SBATCH --time=10
#SBATCH --partition=high
#SBATCH --mem=1G
#SBATCH --job-name=array_demo
#SBATCH --array=1-3

srun echo "Hello from array job $SLURM_ARRAY_TASK_ID"

脚本解析:

  • $SLURM_ARRAY_TASK_ID 是一个环境变量,在每个数组任务中会自动被赋予不同的值(1,2或3)。
  • 提交后,SLURM会生成3个独立的作业任务,每个任务输出不同的信息。
  • 输出日志文件也会被分开,通常命名为 slurm-作业ID_数组任务ID.out

您可以在您的任务脚本(Python、R等)中利用 $SLURM_ARRAY_TASK_ID 来读取不同的输入文件或设置不同的参数。

在Python中获取任务ID示例:

#!/usr/bin/env python3
import sys
job_id = int(sys.argv[1]) # 从命令行参数获取
print(f"This is array task {job_id}")

对应的批处理脚本中这样调用:

srun python my_script.py $SLURM_ARRAY_TASK_ID

总结

本节课中我们一起学习了在高性能计算集群上使用SLURM进行计算的完整流程。我们从并行计算的概念和HPC集群架构讲起,然后逐步深入:

  1. 学会了使用 sinfosqueue 等命令查看集群资源和作业状态。
  2. 掌握了通过 srun 启动交互式作业进行调试和探索。
  3. 掌握了通过编写包含 #SBATCH 指令的脚本,并使用 sbatch 提交非交互式批处理作业
  4. 最后,我们探索了两种实现并行的强大工具:并行步骤用于运行多个不同任务,数组作业用于高效运行大量相同任务。

请记住,在共享的HPC环境中,合理请求资源(不超额请求)并及时取消不需要的作业是一种良好的实践。现在,您可以开始将您的计算任务部署到HPC集群上,充分利用其强大的并行计算能力了!

posted @ 2026-03-29 09:28  布客飞龙II  阅读(4)  评论(0)    收藏  举报