news 2026/10/13 11:06:51

TensorFlow计算图核心拆解:张量、op与Session运行机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TensorFlow计算图核心拆解:张量、op与Session运行机制

1. TensorFlow是谁,为什么从它入手学深度学习

1.1 深度学习框架到底帮你省了什么

先说个最朴素的问题:深度学习框架到底是干什么的,为什么所有学习路线图都会让你先装一个?

如果完全手写一个神经网络,你至少要做三件事。第一件事是定义网络结构,哪一层接哪一层,每层有多少神经元;第二件事是写正向传播的矩阵运算,这一步倒还好,用数学库就能堆出来;第三件事是写反向传播的梯度推导,这个就非常痛苦了——一个三层网络还能推,到了卷积网络、循环网络,手推梯度基本就是噩梦,一个符号错了整个模型都训练不出来。

框架说白了就是把这三件事封装好:你只管描述模型结构,正向传播和反向传播框架自动完成。TensorFlow之所以成为入门首选,是因为它的算子库非常全,从最基本的矩阵乘法到卷积、池化、循环单元应有尽有,而且学习资料和现成案例数量目前仍然是所有框架里最多的。

我当年刚开始学的时候也有个误区,以为框架是“AI工程师才需要的工具”,后来才明白,框架就是深度学习界的“画图软件”——你不是从零造轮子的人,你要做的是画好自己的设计稿。

1.2 为什么单独花一天理解计算图

TensorFlow和其他工具最大的不同,是它引入了一个“计算图”的概念。整个笔记第二天的标题里,Graph、构建图、op这三个关键词其实是全部内容的核心。

如果你去看PyTorch这类框架的入门代码,会发现它和普通Python写法差不多:定义两个张量,加一下,结果就出来了。但早期TensorFlow 1.x不是这样的。它要先定义一张“图纸”,然后另开一个“会话”去执行图纸上的流程,这种设计让很多初学者一头雾水。

我写这篇笔记的时候,用的也正是这个经典版本。当时老版本还停留在1.x时代,新版马上就要推出来了,所以笔记里专门记了一句“关于新版本”。我后来的经验是:哪怕现在新版已经默认即时执行模式,不强制要求每次都手动建图和开会话,把当初计算图这套机制透彻理解一遍,对理解深度学习模型的前向传播、梯度回传、模型保存和部署都是大有帮助的。

第1天笔记已经提过,深度学习的本质是一堆大矩阵不停做乘法和加法。那么TensorFlow的“图”到底是什么?它是怎么把张量“编织”起来的?这就是今天第2天笔记的重头戏。

2. 环境准备与安装实操:Ubuntu、Linux、Mac到底怎么装

2.1 一键装真的好,但虚拟环境才是长久之计

很多新人图省事,直接在系统全局环境下执行pip install tensorflow,当时是装上了,后面做其他项目时就开始遭殃:项目A需要某个库的旧版本,项目B需要新版本,两个版本一冲突,整个Python环境就废掉了。

我在笔记里反复强调的一个工作习惯是:不管用哪个系统,先建虚拟环境,再装东西。用Anaconda创建环境的话,可以把Python解释器、常用科学计算库和TensorFlow一次性隔离起来;不想装Anaconda的话,用Python自带的venv也能达到同样的效果。

虚拟环境本质上就是一个独立的文件夹,里面放着一套隔离的Python环境和包依赖。所有操作都发生在这个文件夹里,与系统全局Python互不干扰。这个方法不只对TensorFlow有用,任何重度Python项目都应该这么搞。

2.2 Ubuntu和Linux安装流程

写这篇笔记的时候,Ubuntu和Linux的安装方式其实非常统一,核心就是一串命令。先确认Python版本,我当时用的是Python 3.6,然后在用户目录下创建虚拟环境,激活它,再用pip安装:

python --version python -m venv ~/tfenv source ~/tfenv/bin/activate pip install --upgrade pip pip install tensorflow==1.15

需要注意的是,如果你打算装GPU版,那命令就变成了pip install tensorflow-gpu==1.15。但GPU版并不是装完Python包就能跑的,你还需要在系统层面把显卡驱动、CUDA工具包、cuDNN库都装好,而且要确保这些版本的匹配关系正确。笔记本这是一天的时间,如果你连显卡驱动都没装过,我不建议第一天就碰GPU,老老实实先用CPU版把代码跑通,后面真正需要加速时再弄GPU环境,是性价比最高的路线。

这里有个实际操作细节:安装完成后,建议立即验证一下能否正常导入模块:

import tensorflow as tf print(tf.__version__)

能打印出版本号,安装才算真正完成。我见过太多人以为pip装完就万事大吉,结果在导入阶段就报错了,原因通常是Python版本与TensorFlow版本不匹配,或者装了多个Python解释器导致pip装到了另一个环境里。

2.3 Mac安装实操

Mac上装的套路和Linux差不多,但有两个环节容易踩坑。一是如果你用的是新款Arm架构芯片的Mac,早期的TensorFlow版本还没有对应的原生包,需要让Python以Rosetta模拟模式运行,性能会打折扣。我当时的方案是直接用它直接支持的CPU版稳定版本,先把流程跑通。

二是老的Intel芯片Mac装老版本TensorFlow时要特别注意Python位数必须是64位,32位环境下直接报illegal instruction一类的错。现在新版本基本没有这个问题了,但这条经验放在当时很救命。

Mac安装流程可以简化为:

python -m venv tfenv source tfenv/bin/activate pip install tensorflow==1.15

和Linux唯一的区别在于激活虚拟环境的命令,Windows上这个命令是activate而不是source。我把这条写上,是因为笔记读者里有相当一批人在第一步就被环境激活卡住了,直接错过了今天所有的内容。

2.4 版本选择的门道:CPU版、GPU版与新老版本

先说CPU版和GPU版的选择逻辑。深度学习训练的主力是矩阵乘法,GPU在并行计算上的优势碾压CPU,所以训练大模型基本离不开GPU。但环境配置成本是阶梯式上升的:CPU版一次pip install就完事;GPU版还要处理驱动版本、CUDA版本、cuDNN版本三个维度的匹配,有一个对不上就报Could not create cudnn handle这类让人头疼的错。

对新学习者的建议是,先用CPU版把代码逻辑、数据预处理、模型评估这些环节跑通,尽量减少变量干扰。CPU版跑小数据集完全没有问题,代码和GPU版几乎一样,差别只在训练速度。

再说新老版本的切换问题。我记得当时TensorFlow官方已经把API方案调整提上日程了,很多老接口正在被废弃。最直观的变化是tf.mul改成了tf.multiply,tf.sub改成了tf.subtract,早期教程里干干净净的Session也慢慢被后来的Keras高层接口覆盖。这也是我在笔记里专门记下“关于新版本”这类信息的原因——看教程的时候一定要看作者用的版本,否则你按1.x的写法在新版环境里跑,第一行导入就报AttributeError。

后来新版本推出后,老写法确实遇到了兼容问题。新版虽然支持tf.compat.v1这种兼容层,但默认已经是即时执行模式。如果你用我当时笔记里的老代码,需要先加上一行:

import tensorflow.compat.v1 as tf tf.disable_v2_behavior()

这在学习的时候是个过渡方案,真正理解框架后,你会发现新版用起来更顺手。

下面这张表把当时常见的版本选择问题整理成了速查信息,建议存一下看代码的时候对照:

对比维度CPU版GPU版备注
安装复杂度低,一次pip完事高,驱动/CUDA/cuDNN都要配CPU版适合新手
训练速度慢,小数据集尚可快,特别是矩阵运算密集时追求速度选GPU版
新老API差异新旧接口有变动新旧接口有变动老写法需加兼容代码
适合阶段入门学习、代码调试正式训练模型学习期不必追求GPU

3. 初识TensorFlow:张量与“选择类型”到底选什么

3.1 张量:带维度的数据集

我从没用TensorFlow之前,光看“张量”两个字就发怵,觉得它是个特别抽象的数学概念。后来才发现,对写程序的人来说,张量就是“多维数组”。零阶张量是一个数,叫标量;一阶张量是一排数,叫向量;二阶张量是一个矩阵;三阶张量就可以理解为“一摞矩阵”,比如一批图片堆在一起。

一个最容易理解的例子是彩色图片:一张彩色图片就是一个三维张量,三个维度分别是高、宽、颜色通道。一张28×28的灰度手写数字图片是二维矩阵;如果一次要把200张这样的图片一起送给模型训练,那就变成了200×28×28的三维张量。

TensorFlow里最常用的张量创建方式是tf.constant,比如:

import tensorflow as tf a = tf.constant(5) b = tf.constant([1, 2, 3]) c = tf.constant([[1, 2], [3, 4]])

第一个是标量,第二个是向量,第三个是矩阵。用tf.shape可以查看张量的形状,这个在后面调试代码时非常常用。

3.2 为什么非要“选择类型”:类型不匹配是新手第一课

很多新手在创建常量时完全不管数据类型,写了tf.constant(1)就认为完事了。然后做矩阵乘法时突然报类型不匹配,完全想不通问题出在哪。

TensorFlow的类型系统和Python不太一样,虽然写起来感觉都是数字,但每个张量都自带一个数据类型标识。你的1是个整数型int32,而1.0是浮点型float32,这两个类型做某些操作时会直接报错。

我在笔记里特意把“选择类型”单独列出来,就是想强调这一点。在TensorFlow里,最常见的类型标签是tf.float32,因为绝大多数神经网络参数都用浮点数表示。常量默认规则是:整数默认为int32,小数默认为float32,你可以通过dtype参数显式指定:

tf.constant(1.0, dtype=tf.float32)

如果两个张量类型不一致,比如一个是int32,一个是float32,把它们强行相加会报错。这时候用tf.cast做类型转换:

x = tf.constant(1) x = tf.cast(x, dtype=tf.float32)

tf.cast的用法就是传入张量和目标类型两个参数,返回一个转换后的新张量。这个技能后面读任何模型代码都会用到,因为不同数据源的默认类型实在太乱了。

3.3 常量、变量、占位符,三个容易搞混的容器

刚开始学TensorFlow的时候,我总把常量和变量混为一谈,以为变量就是“值可以改的常量”。但它们的本质区别非常大,背后的机制直接关系到你能不能把模型训练起来。

tf.constant定义的值在计算图中是固定不变的,它相当于程序里的只读配置。tf.Variable才是真正能“被优化”的容器,模型里的权重和偏置都需要用变量来存,训练过程中要不断修改它的值。

创建变量要这样写:

w = tf.Variable(tf.ones([2, 2]))

一个很容易忽略的坑是:变量在使用之前必须初始化。在旧版TensorFlow里,你必须先执行一次全局变量初始化操作,才能对变量取值,否则会报Attempting to use uninitialized value的错误。

还有一个tf.placeholder,中文叫占位符。它先占住图形的输入位置,等真正运行的时候再通过feed_dict把具体数据喂进去。这个机制对新手来说是最难理解的一环,但也是后期训练模型的必备写法。

4. 核心概念拆解:计算图、op、图构建

4.1 计算图是画图纸,不是跑程序

要理解TensorFlow,必须适应“两段式思维”:“构建图”和“运行图”。构建图阶段,你写代码去搭建一张结构网,这个阶段不会发生真实计算;运行图阶段,你启动一个会话,数据踩着图里设计好的路线流动起来,结果才真正算出来。

这有点像装修房子:你拿着图纸跟施工队说哪里是承重墙、哪里是卫生间,这叫“构建”;施工队真正进场干活、把墙砌起来,这叫“运行”。两者不是一回事。

图里的每一个节点叫做op,是“操作”的意思。图中的“边”就是上一节讲的张量。张量从一个节点流向下一个节点,不断被加工,最终走向输出节点。

举一个最简单的例子:

a = tf.constant(2.0) b = tf.constant(3.0) c = tf.add(a, b)

此时你只是“画了图纸”,程序并没有立刻算出5。真正要让它算出结果,必须再开一个会话去运行。

4.2 op:图中的每个加工动作

op的全称是Operation(操作)。在TensorFlow里,加法是op,乘法是op,矩阵乘法是op,给变量赋值的assign也是op。你可以把计算图理解成一条生产流水线,张量就是流水线上的零件,op就是流水线上的各个工位。

每个op都接收一个或若干张量作为输入,然后做一次加工,产生一个新张量作为输出。比如tf.add接收两个张量,输出它们的和;tf.matmul接收两个矩阵,输出它们的乘积。

理解op是理解后面一切模型的基础,因为神经网络本质就是一大堆op按顺序连接起来的有向无环图。一个最简单的全连接层,就是乘权重、加偏置、过激活函数三个op串在一起。

4.3 Session:让图纸变成实物的钥匙

既然构建完图不计算,那么怎么才能计算?这就需要会话(Session)登场。

在早期版本里,Session对象的职责是给计算图分配计算资源,然后执行图上的任务。代码模式基本上是固定的:

with tf.Session() as sess: result = sess.run(c) print(result)

with语句的好处是运行结束后自动释放资源。如果你忘记关闭会话,程序跑多了就会把内存一点点吃光,尤其是笔记本上跑实验的时候。

一眼就能看出的结果是5.0。但真正让人茅塞顿开的是sess.run的执行机制:它不是“把c拿过来运行”,而是“先在图中找到c这个节点,然后追溯所有让它能计算出来的上下游节点,把这条完整链路一起执行掉”。

这个机制太重要了。举个例子,如果你定义了十个节点,但run的时候只依赖其中三个,那么TensorFlow只会执行这三个节点相关的那部分链路,其他节点一动不动。这就是TensorFlow图执行具备“按需执行”特性的原因。

4.4 为什么要把计算步骤先画成图

稍微了解计算图后,必然会问一个问题:为什么不直接算,非要先画图再运行?

这个问题的答案,正是TensorFlow设计最精妙的地方。把计算过程表示成图,图纸就能被做各种分析和优化。比如你知道两个节点之间互不依赖,那就可以把它们安排到不同的GPU上同时执行,这就是模型并行;训练神经网络要算梯度,图结构可以自动完成从输出节点反向逐层推导梯度的工作;部署模型时还可以对图做减法,裁剪掉训练专用但预测不需要的节点,让模型体积更小运行更快。

用大白话说,图给了TensorFlow动手脚的空间。如果每一步都即时计算,那么优化、分布式、自动求导这些事情的实施难度会成倍上升。虽然新版本已经不需要手动建图和开会话了,但这套图表达的思想依然存在于框架内部,只是被封装得更隐蔽了。

5. 动手构建第一个实际计算图

5.1 从最简单的常量运算开始

光说不练没有意义。我笔记里做的第一个实际操作,就是用三个常量构建一个最简单的计算图,然后让会话跑起来。你可以直接在自己的环境里试一遍:

import tensorflow as tf a = tf.constant(2.0, name="a") b = tf.constant(3.0, name="b") c = tf.constant(4.0, name="c") d = tf.add(a, b, name="d") e = tf.multiply(d, c, name="e") with tf.Session() as sess: print(sess.run(e))

这个图的结构是:a和b先相加得到d,d再和c相乘得到e。那么sess.run(e)会先依赖d,d又依赖a和b,所以整条链路都会计算出来,最终得到20.0。

给节点起名字是个好习惯,后面用TensorBoard可视化计算图时会非常清晰,否则看图上全是无意义的数字节点名,无法定位问题。

我建议你故意改一下,比如把c也放进去,再run一下d,观察只输出5.0而不会算e。这样你能亲手体会到“按需拉取”的机制。

5.2 用可重复利用的节点把图和真实数据接起来

纯常量的计算图,说句实话,价值仅限于演示。真正落地到训练,数据是不断变化的。所以接下来就把placeholder引入图,这样同一个图可以反复输入不同数据。

import tensorflow as tf x = tf.placeholder(tf.float32, shape=[None, 2], name="x") w = tf.Variable(tf.random_normal([2, 1], stddev=0.1), name="w") b = tf.Variable(tf.zeros([1]), name="b") y = tf.matmul(x, w) + b init = tf.global_variables_initializer() with tf.Session() as sess: sess.run(init) # 第一次喂入三行数据 result1 = sess.run(y, feed_dict={ x: [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]] }) print(result1)

这里有几个点要逐一说清。

第一个点是shape=[None, 2]:None表示这个维度不固定,以后每次喂多少条数据都行;2表示每条数据有两个维度,这个必须固定。这是一种非常典型的批处理设计方式。

第二个点是tf.Variable(tf.random_normal(...)):变量在初始化的时候还需要一个初始值,我用的是一个形状为2×1的随机矩阵,这是神经网络中常见的权重初始化方式。

第三个点重点说一下:使用变量后,必须执行初始化操作。代码中init = tf.global_variables_initializer()只是定义了一个初始化操作,并没有真正执行;真正执行发生在sess.run(init)这一步。漏掉这一步就会报变量未初始化错误,是我当时和很多同学踩得最多的坑。

执行之后,你会看到三行结果,每行是每条输入数据经过矩阵乘法和加法后的输出,并且每次运行结果几乎不变。想体验可重复利用的效果,就用另一组数据再喂一次,图还会正常工作,不需要重新构建。

5.3 把图保存下来,以后才能复用和可视化

算完结果直接关程序,图就白建了。笔记中“构建图”这一部分还有个后续,就是图保存。早期版本里保存图的标准写法是:

tf.train.write_graph(sess.graph_def, "./model", "graph.pbtxt")

保存出来的.pbtxt是人类可读的文本格式,你能直接打开看图的拓扑结构。等后面学TensorBoard时,还要用FileWriter把图结构导入事件文件,再在浏览器里以可视化的方式查看。

把图保存下来有非常实际的意义:模型训练完毕后,推理阶段不需要再重新执行构建图的代码,直接从文件加载即可。这等于你用一次构建的图纸,反复生产运算结果。

图的重要程度在新版里有所弱化,能自动处理很多细节,所以你也不必有压力:只要把构建图、喂数据、点按需运行这三件事在手里过一遍,脑子里的建模方式就搭起来了。

6. 常见问题与排查技巧实录

整个教程看下来,最影响信心的是那些光怪陆离的报错。我把笔记里积累的典型问题和解决方案整理成了一张速查表,你日后遇到类似问题可以直接对照,秒判断方向。

报错现象常见原因解决办法
No module named 'tensorflow'没安装,或者装错环境确认虚拟环境已激活,重新pip安装
AttributeError: module 'tensorflow' has no attribute 'mul'写法版本太旧,新版改名用tf.multiply,或用兼容层导入
Attempting to use uninitialized value有变量却没执行初始化结账会话前运行global_variables_initializer
dtype不匹配的报错不同数据类型的张量混合运算用tf.cast统一类型
模型跑一次就报错,但代码看着没问题显存不足或者驱动配置异常小批量、降分辨率或换CPU版跑
训练越跑越慢会话没关,图一直累积用with块管理Session,及时释放

6.1 报错先看类型和版本,别急着搜代码

我见过太多新手,一报错就把错误信息复制到搜索引擎里,然后对着完全无关的解决方案来回试。实际上,TensorFlow的报错信息往往已经说得非常直白:

  • 报错开头如果带AttributeError,基本是Python层面的属性或方法名写错了,去查对应版本API。
  • 如果带InvalidArgumentError,基本是图中的张量维度或类型不匹配,先看数据shape。
  • 如果带tensorflow.python.framework.errors这类内部错误,多半是环境配置或设备问题,而不是你代码逻辑的问题。

读报错的时候,习惯先定位到“哪一行代码触发”,再往上看具体的NOTE和description。我自己的习惯是把第一次报错解决后,把报错信息存到自己的笔记里,过几天回头看,就会形成一套自己的“报错模式识别”。

6.2 一个让我印象深刻的类型坑

我在跑一个线性回归小例子时,明明把输入矩阵准备好了,却在矩阵乘法那一步报错,说“输入类型不一致,期望float32却得到了int32”。我当时看到这行内心是崩溃的,因为我的数据明明是小数。

排查到最后才发现问题出在numpy数组的默认类型上面。我读进来的CSV文件里恰好全是整数,于是numpy自动把它解释成了int64,我这个int64矩阵直接作为feed_dict喂给图的float32占位符,就炸了。

解决方式非常朴素,在喂数据之前加一行类型转换:

data = data.astype(np.float32)

这个坑我记忆犹新,因为它告诉我:TensorFlow不仅要管代码之内的类型,还要管从Python、NumPy进来的外部数据是什么类型。从那天起,我在每个脚本开头都会专门做一次数据类型检查,成本极低,却省掉了大量后续排查时间。

6.3 关于默认图和内存膨胀

还有一个问题,用交互式环境(比如Jupyter)写TensorFlow代码时特别容易遇到:反复执行构建图的代码,默认图里的节点越来越多,内存和显存占用不断上升,最后程序越来越慢。

原因是默认图是全局共享的,你在一个单元格里构建一张图,它不会因为单元格运行完就被自动清空。正确做法是显式新建图:

graph = tf.Graph() with graph.as_default(): # 在这里构建你的计算图 pass

这样每张图都是独立隔离的,用完就释放。如果你用的是新版TensorFlow,这个问题由即时执行和函数跟踪机制重新设计过了,不容易再踩到,但理解和图相关的内存管理模式,对排查很多疑难杂症依然有帮助。

7. 今天的知识地图与继续往下走的路

如果你完整读到这儿,说明已经跟上了第二天的学习节奏,动手环境也搭得差不多了。这一天的内容表面上只有安装、版本、张量、图、op,实际上你在头脑中建立起了一套非常重要的工作模型:先设计计算图,再往图里喂数据,最后让框架按需执行,拿回计算结果。

我强烈建议你现在做三件事来巩固:第一,把第三节和第五节的代码在本地真实运行一遍,不要复制,逐行手敲;第二,用tf.Graph和shape相关函数把你的数据随便组合成一个小图,并打印出每个节点的shape和dtype;第三,遇到报错时先自己判断一下属于“环境问题”还是“代码逻辑问题”,再去找原因。

很多人怕TensorFlow难,其实难度的源头就是“先画图纸再开工”的思想扭转。只要把这一关过了,后面学线性回归、逻辑回归、神经网络结构,都会顺畅得多。我在实际学习过程中最深的一个体会是:不要急着跑别人现成的模型代码,先把自己手中十个以内的节点组合玩明白,你对框架的掌控感会完全不同。

下一篇笔记,按照学习路线就是动手写第一个完整训练脚本了。到时会用到今天反复练习的placeholder、Variable、Session和feed_dict这套组合拳。你可以提前思考一个问题:如果有1000条数据,要怎么设计feed_dict才能让图既能一次处理全部样本,也能每次只处理一小批?想明白这一点,你已经走在理解深度学习批次训练机制的路上了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 11:05:42

MES工艺卡片公式批量导入XHEDITOR:从Excel到网页编辑器的完整实践

做汽车行业MES相关工作的人,多半都遇到过这样一个有点“鸡肋”的需求:工艺卡片要从Excel台账、老Word文件、甚至扫描PDF里,把那些看起来不起眼、但车间老师傅特别较真的公式,成批导入到网页端的工艺编辑器(比如标题里说…

作者头像 李华
网站建设 2026/10/13 11:05:27

Postman脚本加密JSON报文:动态参数与签名避坑指南

我记得年前联调一个对公接口时,同事在本地脚本里跑得好好的,挪到 Postman 里就报验签失败。一开始怀疑算法写错了,后来一行行对比才发现:报文体里有三个动态字段,脚本每次生成的顺序和取值时机不同,导致参与…

作者头像 李华
网站建设 2026/10/13 11:04:57

从结果倒推设计产品方案:目标翻译、路径逆拆与最小闭环

做方案这件事,很多人有个惯性:拿到需求先顺着想。顺着用户场景想,顺着现有功能想,顺着资源限制想,最后排一版看起来哪儿都对的计划。但执行到一半往往才反应过来,整条路从一开始就没指向真正要的结果。我经…

作者头像 李华
网站建设 2026/10/13 11:03:24

PhotoImpact v13老照片修复与设计:百宝箱操作、对象管理与避坑指南

简介:Ulead PhotoImpact v13.0 是一款集图像制作、编修与网页设计于一体的图形处理工具,面向需要修图、制作网页横幅、合成图片素材的设计人员,也适合希望系统学习图像编辑的初学者。它内置方便快捷的克隆与编修工具,配合功能强大…

作者头像 李华
网站建设 2026/10/13 11:03:07

SSH证书签名实现临时免密登录:告别authorized_keys手动管理

不管你是不是刚入行,只要管过几台Linux服务器,一定经历过这种场景:有同事临时需要上一台生产机器看看日志,或者外部合作方要做一次紧急排障。传统做法是让他把公钥发过来,你手动塞到目标机器的 authorized_keys 里&a…

作者头像 李华