1. TensorFlow到底怎么想,2024年还值不值得学
TensorFlow从2015年开源到现在快十年了,这期间深度学习从学术界的小众工具变成了工业界的标配基础设施。你可能在犹豫:2024年了,PyTorch在论文里几乎一统天下,学TensorFlow还有用吗?我的回答是:如果你想去大厂做模型部署、做推荐系统、做移动端推理,TensorFlow依然是绕不开的一环。本文不是教科书式的框架介绍,而是站在一个常年用TensorFlow干活的人的角度,讲讲安装、核心概念、实战训练、生态对比和踩坑经验,尽量让零基础的人也能复现出完整流程。
先说清楚它能干什么。TensorFlow本质上是一个数值计算框架,核心能力是"用张量(Tensor)做自动微分",你可以简单理解成:你定义好一个数学公式(比如神经网络),框架自动帮你算梯度,然后你拿着梯度去更新参数。这个能力让训练神经网络从手推公式变成了"搭积木"一样的工作。它能解决的问题包括图像分类、目标检测、文本生成、推荐排序、语音识别等几乎所有主流机器学习任务。如果你未来想做机器学习工程师、算法工程师,或者只是在项目里需要用到深度学习模型,TensorFlow这套技能树都是值得投入时间爬的。
再说说适合谁。我的观察是,TensorFlow适合三类人:第一类是在工业界做模型落地的人,因为TF Serving、TensorFlow Lite(移动端)和TF.js(浏览器端)这套部署链路目前还是最成熟的;第二类是需要在生产环境里跑大规模分布式训练的人,因为TF的分布式策略历史最久、坑最少;第三类是刚入门的学生,Keras这套高层API的设计对新手非常友好,你不需要理解太多底层细节就能训练出一个能用的模型。当然,如果你专注学术研究、发论文,那PyTorch可能更顺手,这一点后面会专门展开对比。
2. 环境准备:先把TensorFlow装进你的机器
2.1 Python版本与硬件要求怎么选
安装TensorFlow之前,最容易忽略的是Python版本。我见过太多人装了Python 3.13然后跑来问为什么装不上TensorFlow,其实TensorFlow当前的稳定版本只保证支持到Python 3.9到3.12之间,具体的兼容矩阵在PyPI页面上写得很清楚。建议直接装Python 3.10或3.11,这两个版本在兼容性和性能之间平衡最好,我自己的生产环境一直用Python 3.10。
硬件方面,TensorFlow的训练分CPU和GPU两种场景。如果你只是跑跑MNIST、CIFAR这种入门数据集,CPU完全够用,训练一个简单CNN几分钟到十几分钟。但是一旦上了ResNet这种级别的模型,或者数据量到了几十万张图片,CPU训练会让人怀疑人生。这时候需要NVIDIA显卡,因为TensorFlow的GPU加速依赖CUDA和cuDNN这两个NVIDIA闭源库。注意,AMD显卡用不了TensorFlow GPU版的官方支持,苹果的M系列芯片走的是Metal加速,TensorFlow也有对应的插件,但始终是"能用但不够主流"的状态。
有个特别实用的建议:如果你有NVIDIA显卡,先去看显卡支持的CUDA版本,再去对照TensorFlow官方要求的CUDA和cuDNN版本,强行安装不匹配的版本经常会遇到"library not found"或者"could not load dynamic library"这类报错。实操中最稳的办法是直接安装带GPU支持的TensorFlow版本,然后用一个简单命令验证是否真的用上了GPU,这个验证方法我在后面会写。
2.2 pip安装与conda安装的取舍
安装方式不外乎pip和conda两种,我两种都用了多年,说说各自的优缺点。pip安装最简单,一条命令就能装好,而且TensorFlow官方在PyPI上的包更新速度最快,新版本一发布,pip上基本当天就有。我自己日常开发就是用pip,因为虚拟环境管理配合venv或virtualenv已经足够。推荐的做法是:
# 创建虚拟环境,避免污染系统Python python3 -m venv tf_env source tf_env/bin/activate # Windows下是 tf_env\Scripts\activate # CPU版本 pip install tensorflow # GPU版本 pip install tensorflow-gpu # 老版本需要分开装这里想提醒一点:TensorFlow到了2.x时代,"tensorflow-gpu"这个包名已经合并到"tensorflow"里了。也就是说你只要装了tensorflow,它会自动识别本机能不能用GPU,不再需要额外装一个gpu版本。很多教程还在教人先装tensorflow再装tensorflow-gpu,这是老黄历了,装完之后两个包冲突,反而把环境搞坏。
conda安装适合那些需要精确控制CUDA版本的人。conda可以在环境里装指定版本的CUDA Toolkit和cuDNN,不干扰系统级的驱动。这在部署到老服务器时特别好用,因为生产环境往往是"系统驱动很老,但又要跑新模型"这种尴尬场景。具体操作是这样:
conda create -n tf python=3.10 conda activate tf conda install -c conda-forge tensorflowconda的好处是它会自动帮你把CUDA相关依赖计算好,装完基本不用手动配环境变量。缺点就是慢,conda的依赖解析有时候能卡上半天,而且conda仓库里TensorFlow版本更新比PyPI滞后一些,这倒不是致命伤,因为TensorFlow的稳定版本迭代节奏本身不快。
我个人的建议是:本地开发用pip + venv,服务器部署用conda环境或者直接用Docker镜像。TensorFlow官方提供了完整的Docker镜像,里面CUDA、cuDNN全部配好,你pull下来直接跑,这其实是避免环境地狱的最优解,尤其是多人协作的项目,一人一个Dockerfile,永远不用担心环境对不上。
2.3 验证安装:跑通第一个TensorFlow程序
装好之后不要急着开干,先花两分钟验证一下安装是不是真的正常。这一步能帮你区分"装好了但环境有问题"和"压根没装上"两种情况。我每次在新的机器上都会执行这段代码:
import tensorflow as tf print("TensorFlow版本:", tf.__version__) print("GPU设备列表:", tf.config.list_physical_devices('GPU'))如果能看到类似"TensorFlow版本:2.16.1"的输出,说明CPU版装好了。如果你期望用GPU,这里就要看第二行有没有输出。有的话会出现一个类似[PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]的东西,看到这个说明GPU真的能用。很多人只看任务管理器里GPU占用高不高,其实第一次验证就要确认TensorFlow自己能不能看到设备,这一步是最直接的。
再跑一个简单的张量运算,验证自动微分真的能用:
# 定义一个可变量,计算x^2在x=3处的梯度 x = tf.Variable(3.0) with tf.GradientTape() as tape: y = x ** 2 grad = tape.gradient(y, x) print("梯度值:", grad.numpy()) # 期望是6.0输出梯度值为6.0,说明整个数值计算链路没问题。这一步虽然简单,但能在你正式开始项目之前发现很多潜在隐患,比如某台机器的CPU指令集不支持某些指令导致TensorFlow崩溃、Python版本太新导致运行时异常、系统缺少某些动态链接库等等。
3. 核心概念:张量、自动微分与Keras三层抽丝
3.1 张量:TensorFlow的基本数据单位
张量(Tensor)这个概念是理解TensorFlow的地基。你可以把它理解成一个多维数组,或者更形象一点:标量是0维张量(就是一个数),向量是1维张量(一排数),矩阵是2维张量(一个表格),再往上就是三维、四维甚至更高维的"数据盒子"。在图像处理中,一张彩色图片通常表示成一个三维张量,形状是(height, width, channels),三个通道分别对应红绿蓝。一批图片就变成了四维张量,形状是(batch_size, height, width, channels)。这个batch的概念后面会反复遇到。
在TensorFlow里创建张量的方式很灵活,最常用的几种是:
import tensorflow as tf # 从列表创建 t1 = tf.constant([1, 2, 3, 4]) # 创建全零张量 t2 = tf.zeros([3, 4]) # 创建随机张量 t3 = tf.random.normal([2, 2], mean=0.0, stddev=1.0) # 创建变量(可以更新的张量) t4 = tf.Variable(tf.zeros([10, 10]))其中tf.Variable和tf.constant的区别要特别注意:constant是不可变的,你在训练过程中更新权重用的是Variable。神经网络里的权重和偏置本质上都是Variable,训练的过程就是不断改变这些Variable的值。
关于张量,新手最容易困惑的是维度。我建议你在练习的时候多打印shape属性和ndim属性,慢慢建立空间感:
print(t1.shape) # (4,) print(t2.shape) # (3, 4) print(t3.ndim) # 2 print(t4.dtype) # float32 - 默认数据类型另一个容易踩坑的点是dtype。深度学习里默认用float32精度,但如果你从文件里读数据时用了整数类型,喂给模型之后经常会报类型不匹配。养成习惯,所有输入数据都显式转换成float32,能省掉很多莫名其妙的报错。用tf.cast(x, dtype=tf.float32)转换即可。
3.2 自动微分:反向传播的幕后原理
训练神经网络的核心就是不断调整参数,让损失函数变小。调整的方向是梯度的负方向,也就是传说中的梯度下降。问题是,一个网络动辄几十万上百万个参数,手算每个参数的梯度根本不现实。TensorFlow用了一个叫"自动微分"的技术,让它在你定义好的计算图上自动求出所有参数的梯度。
TensorFlow 2.x的实现方式是GradientTape。你可以在一个"磁带"上下文中执行运算,之后回溯这段运算记录,自动计算梯度。
# 定义一个变量 w = tf.Variable(tf.random.normal([2, 1])) # 输入和真实标签 x = tf.constant([[1.0], [2.0]]) y_true = tf.constant([[3.5], [5.0]]) with tf.GradientTape() as tape: y_pred = tf.matmul(x, w) # 线性预测 loss = tf.reduce_mean(tf.square(y_pred - y_true)) # MSE损失 grad = tape.gradient(loss, w) print("梯度:", grad.numpy())这段代码的含义是:我们有一个线性模型 y = x * w,给定两个数据点,计算出损失对权重w的梯度。有了梯度之后,手动更新权重就是:
w.assign_sub(0.01 * grad) # w = w - 0.01 * grad当然,真实项目中你不会手动更新,而是交给优化器(Optimizer)。但理解GradientTape的原理很重要,因为遇到自定义训练循环时,你会直接和它打交道。默认情况下GradientTape只记录一次前向传播,如果你需要计算二阶梯度,就得把persistent=True打开,用完再删掉,否则内存会泄漏。
3.3 Keras高层API:为什么新手也能快速上手
在TensorFlow 2.x之前,写一个模型要全局定义Graph、Session、Placeholder,那套代码对新手极其劝退。后来谷歌把Keras集成成了TensorFlow的高层API,整个用法变成了"像搭积木一样搭网络"。Keras的核心理念是"Sequential"顺序模型,你只要把网络层一层一层堆叠起来就行:
from tensorflow.keras import layers model = tf.keras.Sequential([ layers.Dense(64, activation='relu', input_shape=(784,)), layers.Dense(64, activation='relu'), layers.Dense(10, activation='softmax') ])这个模型就是一个三层全连接网络:输入层784个神经元对应784维数据(比如拉平的28x28像素图),中间两个隐藏层每层64个神经元,最后一层10个神经元对应10个类别(手写数字0-9)。Dense层就是全连接层,每个神经元和上一层的所有神经元都有连接。激活函数relu负责引入非线性,没有它的话多层线性层叠起来还是线性的,网络表达能力会大打折扣。softmax把输出映射成10个类别的概率分布。
编译模型的代码也很直观:
model.compile( optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'] )这里优化器adam是最常用的自适应学习率优化器,不用手动调学习率就能收敛得不错。损失函数sparse_categorical_crossentropy适合整数标签的分类任务,如果你的标签是one-hot编码向量,就用categorical_crossentropy。这两个的区别很多人搞混,其实就一句话:sparse版本接受整数标签(比如数字7),非sparse版本接受one-hot向量(比如[0,0,0,0,0,0,0,1,0,0])。
4. 实战项目:用TensorFlow训练一个图像分类模型
4.1 MNIST数据集的加载与预处理
理论讲了半天,不如实际跑一个项目来得实在。这里我挑MNIST手写数字识别入门,这个数据集确实是"深度学习领域的Hello World"——任务足够简单,数据量足够小,CPU上几分钟就能训练出一版不错的结果,非常适合用来检验整个流程是否跑通。
MNIST数据集在TensorFlow里可以直接下载,不需要去官网找数据:
from tensorflow.keras.datasets import mnist (x_train, y_train), (x_test, y_test) = mnist.load_data() print("训练集形状:", x_train.shape) # (60000, 28, 28) print("测试集形状:", x_test.shape) # (10000, 28, 28) print("标签样例:", y_train[:10]) # [5 0 4 1 9 ...]加载出来的训练集有6万张28x28的灰度图,测试集1万张。原始像素值的范围是0到255,这个范围直接喂给神经网络的话,数值太大会让梯度计算不稳定。所以要做一个归一化处理,把所有像素值缩放到0到1之间:
x_train = x_train.astype('float32') / 255.0 x_test = x_test.astype('float32') / 255.0接下来要考虑数据形状的问题。上一节里我写了一个输入形状是784的模型,而MNIST的原始形状是28x28,两种处理方式都可以。第一种是使用全连接网络,需要把图像拉平成784维向量;第二种是使用卷积神经网络(CNN),直接保留二维空间结构。这里先演示拉平操作,用全连接网络跑通流程:
# 拉平成784维向量 x_train_flat = x_train.reshape(60000, 784) x_test_flat = x_test.reshape(10000, 784) # 与模型输入对齐 model = tf.keras.Sequential([ layers.Dense(64, activation='relu', input_shape=(784,)), layers.Dense(64, activation='relu'), layers.Dense(10, activation='softmax') ])这里有个细节值得说:input_shape只写在第一层,后面的层会自动推断输入维度,不用每一层都写。如果你用的是类似ResNet这种超深网络,手写input_shape很容易出错,Keras会自动帮你完成维度匹配。
4.2 模型搭建与编译参数选择
模型搭好之后,下一步是编译和训练。compile并不是执行训练,它只是做了一些内部准备工作,比如确定优化器、损失函数、评估指标。fit才是真正开始训练的地方:
model.compile( optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'] ) history = model.fit( x_train_flat, y_train, epochs=5, batch_size=32, validation_split=0.2, verbose=1 )几个参数逐一说明:epochs表示训练轮数,每轮都把全部训练数据过一遍;batch_size表示每批处理多少张图片,这里32表示模型每次看到32张图片后更新一次参数。validation_split=0.2表示从训练集里切出20%作为验证集,用于观察模型在没见过的数据上的表现。
运行之后你会看到每个epoch的loss和accuracy,以及validation_loss和validation_accuracy。这里我要强调一个非常重要的判断逻辑:训练集准确率会稳步上升,但你要重点观察验证集准确率,如果验证集准确率不再上升甚至开始下降,而训练集准确率还在涨,那就说明模型过拟合了,也就是"背题背得太死,换一套题就不会了"。解决办法包括增加数据量、使用dropout层、减小模型复杂度或者早停。
训练完成后的评估很简单:
test_loss, test_acc = model.evaluate(x_test_flat, y_test, verbose=0) print("测试集准确率:", test_acc)一个标准的全连接网络在MNIST上的准确率大约在97%-98%之间。如果你换成卷积神经网络,直接干到99%以上不是问题。这就是为什么实际项目里几乎都用CNN来处理图像问题——全连接网络忽略了像素的二维空间结构,而CNN通过卷积核能捕捉局部空间特征。
4.3 训练过程监控与回调机制
fit时候的verbose=1会在控制台打印每个epoch的结果,但这只适用于调试。在实际项目中,训练过程动辄多小时甚至数天,这时候需要用到回调机制来监控和干预训练过程。最常用的回调有四个:EarlyStopping(早停)、ModelCheckpoint(保存最好模型)、ReduceLROnPlateau(自动降低学习率)和TensorBoard(可视化)。
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint, ReduceLROnPlateau callbacks = [ EarlyStopping(monitor='val_loss', patience=3, restore_best_weights=True), ModelCheckpoint(filepath='best_model.h5', monitor='val_accuracy', save_best_only=True), ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=2) ] history = model.fit( x_train_flat, y_train, epochs=50, batch_size=32, validation_split=0.2, callbacks=callbacks )这里的关键思路是:不要设置一个特别大的epochs然后干等,而是设置一个合理的上限比如50,让EarlyStopping在验证集指标连续几轮不提升时自动停止。patience=3表示连续3轮验证损失没有改善就停,同时restore_best_weights=True会把模型权重回滚到验证损失最低的那个epoch,防止你最后拿到的模型不是最好的那一个。ModelCheckpoint会实时保存表现最好的模型,即使训练中途崩溃,你已经有一个可用模型了。ReduceLROnPlateau会在验证损失进入平台期时把学习率减半,帮助模型跳出局部最优。
5. TensorFlow与PyTorch的2024年竞争与选型
5.1 两者在开发体验上的核心差异
既然2024年的热词总是在对比TensorFlow和PyTorch,那就好好聊聊这个话题。我两个框架都用过不少,最直观的差异是"计算图生成方式":PyTorch是动态图,你写代码的时候图就在实时构建;TensorFlow 2.x虽然是eager mode默认动态,但它骨子里还是保留着静态图的基因,到了生产部署时,会把模型转换成静态图(SavedModel)来提升性能。对研究者来说,动态图调试起来非常爽,打个断点就能看到中间每一层的值;对部署者来说,静态图的好处是推理速度快、内存占用稳定。
开发体验上,PyTorch的代码更"Pythonic",写起来和写普通Python代码没什么区别。TensorFlow这边,Keras封装之后其实也不差,但遇到自定义训练循环、自定义层的时候,GradientTape的写法会比PyTorch的autograd多一点显式的仪式感。不过TensorFlow有一个PyTorch没有的优势:Keras不但能跑在TensorFlow后端上,还能选择JAX或PyTorch作为后端,这个设计在2024年逐渐得到了社区认可,相当于给Keras注入了新的生命力。
学术领域,PyTorch这几年确实占据优势。CVPR、NeurIPS这些顶会的论文代码,十篇里可能有七篇是PyTorch写的,这主要是因为PyTorch在动态图和调试体验上更贴近研究节奏。但你要注意,学术优势不等于工业优势,谷歌内部和海量的生产系统里,TensorFlow依然占有不小的份额。如果你的目标是找工作,研究向岗位可能更好要PyTorch,但是工程向岗位、部署岗位,TF的经验会非常值钱。
5.2 生产环境部署角度怎么选
部署是一个常常被新手忽略的环节。实验室里训练了一个好模型,你不可能永远放在notebook里跑,总是要放到服务端去跑推理。这个环节TF的优势就非常明显了:TF Serving可以直接加载SavedModel格式的模型,对外提供gRPC和HTTP接口,不需要额外写推理服务代码,而且支持模型版本管理、请求批处理、GPU流水线。PyTorch这边虽然有TorchServe,但成熟度和文档相比TF Serving还是有差距,我自己在实际部署经验中明显感觉到TF Serving更省心。
移动端和嵌入式这边,TensorFlow Lite是事实上的主力选手。不管你是Android还是iOS,要把模型塞进手机里跑,TFLite的量化工具链最成熟,压缩后的模型体积可以缩到大约原来的四分之一,而且推理速度损失很小。PyTorch的移动端方案目前是ExecuTorch,年轻人还在不断迭代中,稳定性需要时间验证。网页端TensorFlow.js同样很能打,很多浏览器里的AI应用都是它做的。所以你看,如果一个公司要做服务端推理、手机端部署、浏览器端演示,TensorFlow这一条龙服务几乎是唯一"全都成熟"的选择。
5.3 2024年趋势:到底该学哪个
如果硬要我给一个结论,我会说:入门随便学一个,学透比选哪个更重要。底层的机器学习和深度学习原理是不分框架的,你学会用Keras搭一个CNN,换成PyTorch写一个一样的网络也就是一个晚上的事。真正拉开差距的是对模型本身的理解——损失函数为什么这么设计、正则化手段什么时候用、数据增强怎么做、学习率怎么调。框架只是表达这些思想的工具。
往实际方向说,如果你有条件,我建议两个都碰一碰。先拿TensorFlow/Keras入门,因为它的API更抽象,学起来负担小;等动手能力上来了,再看PyTorch源码风格的实现,会看到很多底层细节,体会更深。我自己就是先TF后PyTorch,回头看是值的,两条路都走过的人不会盲捧某个框架,不会被社区情绪带着跑。而且面试的时候,能说出"两个都写过,各有优劣",也显然比单恋一枝花更让面试官认可。
6. 常见问题排查与性能调优
6.1 安装阶段的高频报错
安装TensorFlow时最容易出现的报错是"ModuleNotFoundError: No module named 'tensorflow'",这时你要区分是包真的没装还是Python环境不对。很多人明明用pip装了,但命令行里用的python是系统自带的那个,和pip对应的不是同一个Python,这个问题在Linux上尤其常见。解决办法是先看which python和which pip指向哪里,确认一致之后再操作。
还有一个高频问题:装了GPU版但运行时报错"could not load dynamic library 'libcudnn.so.8'"。这是CUDA和cuDNN版本不匹配导致的。TensorFlow对CUDA版本有严格要求,而且不同TF版本要求的CUDA版本还不一样。最省事的排查方法是用tf.test.is_gpu_available()或者tf.config.list_physical_devices('GPU')来看TensorFlow到底能不能发现GPU。如果报错提到了某个so文件找不到,用ldconfig -p | grep cudnn查看系统里实际装了哪些版本,然后去NVIDIA官网下载对应的版本装上,一般就能解决。
6.2 训练慢、显存不足怎么办
训练慢的原因分很多种,CPU训练的瓶颈基本都在数据加载和矩阵计算,GPU训练的瓶颈往往在数据管线和GPU等待。有一个特别常见又被很多人忽视的点是:数据要用tf.data.Dataset而不是直接用Python的for循环往模型里喂。前者支持预取、并行、缓存,让GPU在处理一批数据的时候,CPU已经在准备下一批了,利用率能明显提升。
# 使用tf.data构建高效数据管道 dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train)) dataset = dataset.shuffle(buffer_size=10000) dataset = dataset.batch(64) dataset = dataset.prefetch(tf.data.AUTOTUNE)显存不足(Out of Memory)是深度学习的经典难题。遇到它,策略从易到难排列:降低batch_size(这是最直接有效的办法);减少输入图像的尺寸;把模型里的浮点精度从float32降到float16或bfloat16,显存占用近乎减半;用梯度累积技术,小batch跑多个batch后攒够等效大batch再更新参数。如果模型本身就太大了,就换成更轻量的架构,比如从ViT-B换成ViT-S。
6.3 模型过拟合的预防手段
过拟合的表现是训练集准确率很高,但验证集或测试集准确率上不去。除了上一节提到的EarlyStopping之外,常用的预防手段有几种。第一种是Dropout层,它在训练时随机让一部分神经元失活,相当于每次训练都在用不同的子网络组合,能有效提高泛化能力。Keras里加一层dropout非常方便:
model = tf.keras.Sequential([ layers.Dense(64, activation='relu', input_shape=(784,)), layers.Dropout(0.5), layers.Dense(64, activation='relu'), layers.Dropout(0.3), layers.Dense(10, activation='softmax') ])第二种是数据增强,对图像数据做随机旋转、平移、缩放、水平翻转,让模型看到更多样化的样本。Keras里也有内置层:
data_augmentation = tf.keras.Sequential([ layers.RandomFlip("horizontal"), layers.RandomRotation(0.1), layers.RandomZoom(0.1), ])第三种是权重正则化,L1或L2正则化会惩罚特别大的权重值,使模型倾向于使用更小的权重,从而抑制过拟合。在Keras中对Dense层传入kernel_regularizer参数即可。这三种手段通常配合使用,没有一种万能的方案,经验是先从数据增强开始,然后加dropout,最后再考虑权重正则化。
写在最后的一点个人体会
TensorFlow这几年被讨论得很多,但真正深入用过的人会有自己的判断。我的感受是,框架选型这件事本质上是你做事的"手感"和"环境"问题,而不是选一个"永远正确"的答案。TensorFlow的优雅之处在于它把从数据到模型的完整生产链路都打通了,这一点是很多论文框框架给不了你的实际价值。训练一个模型只是起点,真正难的往往是那个模型怎么稳定地跑到线上,怎么在移动设备上保持低功耗、高速度,这些问题TensorFlow的生态给了我很扎实的答案。如果你已经在学习深度学习的路上,不用纠结太多"要不要换框架",先把一个框架的整个生命周期摸透,从安装、训练、调参、保存模型、部署到性能优化,跑通一整个项目,再去横向对比,那时候你的体会会比我写的这些更深刻。