最近工作内容陷入了瓶颈,不知道自己想干啥了,总会有些重复性较强的工作。虽然也有些空闲时间看看其他东西,但发现看的东西越多,越发感到迷茫。不清楚自己该学什么,该看什么东西,或者说对自己未来的发展道路、发展方向有一点迷茫,不确定未来方向。
顺其自然地,有一些想法,不吐不快罢,也顺便讲下自己的一些心得体会,希望能帮助大家少走些弯路。
个人之言,也不是啥大佬哈,有相似或不同想法的,欢迎各位留言交流。
初识AI
5年前,刚上研究生的时候,AI、深度学习、人工智能刚开始火起来。对深度学习啥也不懂的我,不知从哪儿找了李宏毅那个时候比较火的一个介绍深度学习的PPT,叫做《一天搞懂深度学习》,恐怖如斯。整整300页,我断断续续看了一天,虽然有点蒙逼,总算对深度学习有了一点点了解。
当晚回去立马百度了下该学什么,看了很多前辈的帖子,最后从闲鱼上淘了几本书:
- 《周志华的机器学习》
- 《机器学习实战》
- 《TensorFlow实战Google深度学习框架》
- 《深度学习》(大家熟知的圣经)
这几本书之前有介绍过,这里再提下,《周志华的机器学习》都是理论,《机器学习实战》主要是代码实战,这两本书可以配合起来看。然后再看《深度学习》圣经这本书,差不多会对机器学习、深度学习有一定的理解,这些书放到现在都依然合适。
其中最没用的就是《TensorFlow实战Google深度学习框架》,那会Pytorch还不出名,大家都是直接上手TensorFlow,跟着书或者教程学习很费劲,这与TensorFlow静态图的设计有很大关系(不过实话实说TF的静态图设计很优秀,比如dynamic分支的实现),加上TF各种经常变化的API,学起来难度极大。
偶然一个机会看到Pytorch(那会Pytorch刚出来0.2版本),看了下示例代码,感觉很清爽,学了一阵顺利复现了一个基于keras的小项目,就认定Pytorch了。
后来Pytorch出来0.4、1.0,到现在的1.12。无论是在学术界还是工程界,Pytorch相比TensorFlow来说已经很不差了,强烈建议新入门的小伙伴直接上手Pytorch。至于TF1或者TF2好不好用,智者见智仁者见仁,我的建议还是Pytorch。
归根结底,不论是Pytorch还是TensorFlow都只是你学习的一个工具,而且这俩库都很牛逼,哪个你用的舒服,就用哪个就对了。不顺手就扔掉,只不过现在tf1还是tf2可能都没有Pytorch顺手,所以用Pytorch上手的人更多,更有甚者,import torch as tf
除了上述这些资料,还看了其他的书和资料:
- 程序员的数学系列(线性代数、数学、概率论)
- OpenCV系列(星云大佬、OpenCV3官方文档)
- C++各种乱七八槽的书(primer、effective、more effective)
- 图像处理相关的书
就这样看看学习视频、看看相关的书、看看代码,学一段时间就入门了。
2017年那会资料就挺多了的,现在2022资料只会更多,不过伴随而来的就是不知道看哪个了,可以按照我的建议来看,也可以自己按照自己喜欢的看,其实看啥都能入门,就是是否系统、或者学习时间快慢的问题,没必要纠结非要最好最快的,行动起来才是关键
深度学习 && CV
初步入门之后,决定做图像方向的深度学习,无论是那会还是现在,CV深度学习的热度和资料都比NLP要高不少,虽然学习难度低一些,但学习的人也很多,同样很卷。
所以说选择很重要。一开始选择Pytorch上手算是比较正确的选择;而一开始学CV,最近几年就不是很吃香了,除了CV本来就非常卷,另外因为transformer的大火,很多NLP、语音识别类的应用也随之不断冒出来了,生态都很不错。很多比赛也是搞一些transformer系列的模型,而很多CV类的模型,比如检测场景,做的人太多了,已经有点审美疲劳了。
现在来看,CV方向还是有很多有意思的项目,比如mediapipe,里头集成了很多有意思的CV应用,已经覆盖大部分场景了。NLP的话,我本身也对transformer感兴趣,一直没有时间好好看一个transformer的项目,近期打算好好看下。
话说回来,在入门后,后续又看了些比较有名气的课:
- 吴恩达的机器学习课
- CS231n
可能还看了别的但是不记得了,印象比较深的就这俩,吴恩达的课太偏理论,看的我快睡着了;CS231n的课质量很高,干货超多,作业也很有质量,这个是强烈推荐初学者去过一遍的,不过难度会稍微大一点。
然后也可以跑几个项目,我是先用Pytorch复现了几个用keras搭的项目,然后自己跑了跑一些检测模型(那会比较火的是faster-R-CNN、Mask-R-CNN、YOLOv1-YOLOv3、SSD等),也有一些其他的风格迁移、GAN、图像分割相关任务,总之都过一遍就了解了差不多了。
现在也是一样,无非就是换了一批明星,可能是YOLOX、YOLOv5、Centernet、CornerNet等等。其他任务也是类似,深度学习领域模型发展的也很快,一年一个baseline,但是我对这些设计这些算法不是很感兴趣(虽然人家设计确实巧妙),哪个好就用那个呗。
大概就是这些,我那会没有报任何收费的培训班,好像从闲鱼买过一些盗版课(记得是优达学城的),其他的就都是公开免费的了,再推荐一次CS231N,这门课的质量真的是很高,英文好的建议直接看英文版。
关于算法工程师和训练
在实际业务中,我们会针对场景训练模型,不过大部分的业务场景,比如说你要实现一个检测人的模型,我们要做的其实就三件事儿:
- 收集数据(从公司的query中找,通过脚本或者人力来筛选)
- 整理数据、标注数据、制作label
- 挑选模型进行训练、调优
其实吧,收集数据和整理数据花费的时间最多,而训练在你熟悉或者有自己的一套框架之后,分分钟搞定。所以真实情况就是这样:
- 收集数据和整理数据制作label,每个新任务新场景都会重头来一遍,不过如果这个流程熟悉了,你有脚本可以自动化了,其实也挺快的
- 配环境、解环境bug;一开始配环境比较耗时,配好之后下次就直接用了,或者,人生苦短,我用docker
- 设计模型,做实验,分析loss,再跑几个实验。大部分任务你直接套一个框架(yolo系列、centernet系列、fcos系列),初版没什么问题,剩下的就是根据产品反馈的badcase来针对性优化了
当这套框架定型之后,大部分新的业务来了,直接按照流程来整体进度是很快的,唯一慢的步骤可能是标注,毕竟标注数据是无法完全自动化的。
其实训练这个活,相关领域你熟悉了,之后的大部分任务都可以快速上手,没啥难度。假如遇到个新的任务,直接github上找个开源库clone下来根据实际业务修改下就行了。虽然说可以自己实现吧,但现实中没有时间让你慢慢搞慢慢预研的。怎么快怎么来,如何快速产出才是最重要的。
至于算法能不能搞,行不行,卷不卷,我的看法是一直很卷。而且因为疫情,现在几乎所有岗位都缩减不少,目前找工作来说难度可是真的大(真的大)。算法肯定还是有需求,门槛的话,没有那么高,训练模型这一整套流程,只要有点基础熟悉起来还是很快的。至于开发新的算法,说实话确实很牛逼,只是大部分场景下不需要,很多业务场景算法做到极致还是比较难的,从0-90提升好说,从98-99就难的很了。大部分的瓶颈也在训练数据,模型的改动后期对精度的影响微乎甚微。
相关的话题,感兴趣的可以看我之前写作的两篇:
最近也给我推送了一些新闻,也不知道是真是假。不过可以肯定的是,不管是阿里的达摩院、华为的2012实验室啥的,我问了一些在里头的朋友,搞的和咱们正常算法工程师相差不大。人家可能更注重发paper,搞预研,咱们可能更注重实际业务需求,但都是用Pytorch呀、TF呀,跑一些算法,改改结构、弄弄数据,都差不多。
总的来说,算法工程师这个需求还是有的,毕竟深度学习在CV和NLP领域都有很多很好的应用。会用、用的好、会根据实际场景选取不同的模型,产出模型并且实际部署应用起来,会这些就可以满足算法工程师的基本要求了。
AI部署
在工作还有在学校的一段时间,我都做了一些部署的工作,也写了几篇关于部署的文章(当然还有有很多坑没填):
AI部署简单来说就是将训练好的模型放在各种平台运行,不过:
- 平台很多(PC端、移动端、服务器、编译端侧、嵌入式)
- 模型种类也很多(检测、识别、NLP、分类、分割、GAN)
- 要求也很多(速度快、稳定、功耗低、占用内存小、延迟度)
- 场景也很多(单模型、多模型分时调用、多模型pipeline、复杂模型前后处理pipeline)
- 会遇到各种问题(结果对不上、动态库ABI不兼容、性能问题)
部署场景多种多样,和算法相比更偏向于实际一点,好的算法能够解决问题,形成一套方案,但是方案的实施,就需要部署了。现在部署的概念我认为其实很杂很泛,你训好了一个模型,用flask + pytorch搭了一个服务给外部调用,就是部署;你把一个模型转化为TensorRT,然后写了个C++服务跑起来,也是部署;你把Pytorch的模型迁移到Caffe中烧录到板子里头,也是部署。
部署不一定非要写C++,也不一定非要使用TensorRT这种高性能库,不过既然你都部署了,性能当然是越高越好嘛。要深入底层去优化模型,那么C++和一些其他的高性能库就少不了了。
部署我认为当下还是很重要的,各种需要部署AI模型的设备:
- 手机(苹果的ANE、高通的DSP、以及手机端GPU)
- 服务端(最出名的英伟达,也有很多国产优秀的GPU加速卡)
- 电脑(intel的cpu,我们平时使用的电脑都可以跑)
- 智能硬件(比如智能学习笔、智能台灯、智能XXX)
- VR/AR/XR设备(一般都是高通平台,比如Oculus Quest2)
- 自动驾驶系统(英伟达和高通)
- 各种开发板(树莓派、rk3399、NVIDIA-Xavier、orin)
- 各种工业装置设备(各种五花八门的平台,不是很了解)
- 其他等等
坑都不少,需要学习的也比较杂,毕竟在某一个平台部署,这个平台的相关知识相关信息也要理解,不过有一些经验是可以迁移的,因此经验也比较重要,什么AI部署、AI工程化、落地都是一个概念。能让模型在某个平台顺利跑起来就行。
部署也不是什么方向,或者说,公司招人的时候也不会搞个“AI部署工程师”的岗位,不像后端、前端这种相对比较固定,职责相对比较专一。而部署呢,相对来说干的活会比较杂一些:
- 中小公司来说,算法工程师也会做部署的事情,毕竟也是工程师嘛
- 大公司来说,会拆的比较细。除了算法工程师,其他岗位,如深度学习工程师、AI-SDK工程师、AI算法系统工程师、端智能应用工程师、AI平台开发工程师、AI框架工程师、高性能计算工程师、AI解决方案工程师等等都会重点做部署的工作,相比于传统的算法工程师,他们一般不训练模型,只会针对模型进行部署或者写一些与模型部署密切相关的组件等等
- 其实不管大公司还是小公司,关于部署的界限也没有那么死,要灵活起来也是非常的灵活
虽然比较杂吧,但这是确确实实的实际落地的工作,不管怎么吹,模型跑到实际的设备上才是最重要的。关于这方面,脏活累活也有很多,比如:
- 模型能不能转成功、能不能跑起来,跑起来之后的速度、精度、占用内存有没有问题
- 模型这个算子在这个平台上没有实现,怎么办,怎么解决,怎么workaround
- 硬件版本、软件版本、SDK版本,匹配不匹配,各种换各种试
- 遇到各种奇奇怪怪的bug,比如多个模型抢占资源,比如某个模型会core
等等等等。
说了这么多,其实我目前也不清楚自己最感兴趣的方法到底是什么,虽然平时的工作也与上面介绍的有一些关系。接下来我也会梳理下自己曾经做过的一些小项目,整理出来,可能是一系列文章,也可能是一个开源库,总之,我再想想。
AI编译器
AI编译器是我一直向往的,原因也很简单,大家都会对未知的、新鲜的东西感兴趣。不过我这辈子可能也就是向往了哈哈。
AI编译器涉及到的技术栈很多,基本的深度学习、编程语言、编译原理、计算机系统原理,再细分C++、编译优化、函数式编程、LLVM等等,需要看的东西很多,没有系统学习过的直接上手学习难度很高。
2022年,AI编译器比之前火了不少,大家听说过的可能有torchscript、Glow、XLA、TVM、MLIR、TensorRT等等。我一开始接触的是TVM,那会并不是很懂AI编译器具体是干啥的,只知道比较牛逼高级一点。简单来说,就是加载一个模型文件(比如ONNX模型),然后输出包含网络结构的序列化好的运行包,我们可以在自己的应用上包含这个和对应AI编译器的运行时so,就可以推理运行了。
再具体点,和编译器类似,AI编译器输入神经网络(前端代码),进行一些优化(计算图优化、公共表达式合并、loop unrolling等),输出可以加载运行的网络文件(可执行文件)。一般来说经过AI编译器优化的神经网络,速度会比直接在原始框架要快一些(Pytorch、TensorFlow),究其原因也是AI编译器所做的一些图优化工作和生成的算子性能要比原生的框架实现要好。
那会也写过两篇介绍TVM的文章:
AI编译器很像黑盒子,一开始用的时候也不管那么多,总之能让自己的模型性能提升就是好事儿,后续渐渐深入研究了一下,学问真的不少。很多算子优化的细节,如果对指令集或者编译器不了解的话,肯定是一头雾水,还是需要花时间研究一下,不需要太深,知道其大致工作原理就可以了,具体的细节什么的,有需要的时候再重点看。
举个简单的例子,TVM在优化模型的时候,会按照设定的规则搜索优化空间,而这个规则我们有必要了解下。可以使用TVM中的TVMScript,类似于python的语法去写kernel(指高性能、手写的CPU或者GPU算子),运行在各种硬件平台,比如我们写一个简单的向量加法:
# 以下是一个简单的向量相加 @tvm.script.ir_module class Vector: @T.prim_func def main(A: T.Buffer[(256,), "float32"], B: T.Buffer[(256,), "float32"], C: T.Buffer[(256,), "float32"]) -> None: # function attr dict T.func_attr({"global_symbol": "main", "tir.noalias": True}) for i0 in T.serial(256): with T.block("C"): x = T.axis.spatial(256, i0) T.reads(A[x], B[x]) T.writes(C[x]) C[x] = A[x] + B[x] # 写好kernel后,使用tvm.build编译 mod = tvm.build(Vector, target="llvm")
这个kernel使用LLVM进行编译,运行在CPU端。在调用tvm.build
后会调用LLVM编译器进行编译,你编译的最终代码性能和你写的这个kernel有很大关系,我们可以通过打印LLVM-IR来查看:
# 查看TVM生成的LLVM-IR print(mod.get_source())
原始的没有使用vector指令只使用标量指令的IR如下:
; Function Attrs: nofree noinline norecurse nosync nounwind define internal fastcc void @main_compute_(i8* noalias nocapture align 128 %0, i8* noalias nocapture readonly align 128 %1, i8* noalias nocapture readonly align 128 %2) unnamed_addr #1 { entry: %3 = bitcast i8* %2 to <4 x float>* %wide.load = load <4 x float>, <4 x float>* %3, align 128, !tbaa !113 %4 = getelementptr inbounds i8, i8* %2, i64 16 %5 = bitcast i8* %4 to <4 x float>* %wide.load2 = load <4 x float>, <4 x float>* %5, align 16, !tbaa !113 %6 = bitcast i8* %1 to <4 x float>* %wide.load3 = load <4 x float>, <4 x float>* %6, align 128, !tbaa !116 %7 = getelementptr inbounds i8, i8* %1, i64 16 # 省略很多 类似的读取+加法+赋值操作 store <4 x float> %443, <4 x float>* %447, align 16, !tbaa !119 ret void }
如这时候使用TVM的Schedule,来将我们刚才写的这个script中的重要loops进行向量化,也就是将这个操作的C维向量化:
# TensorIR 使用schedule sch = tvm.tir.Schedule(Vector) # 得到那个名称为 C 的loop block_c = sch.get_block("C") # 获取loop的维度 (i,) = sch.get_loops(block_c) # 向量化这个维度 sch.vectorize(i)
向量化之后的TVMScript为:
@tvm.script.ir_module class Module: @T.prim_func def main(A: T.Buffer[(256,), "float32"], B: T.Buffer[(256,), "float32"], C: T.Buffer[(256,), "float32"]) -> None: # function attr dict T.func_attr({"global_symbol": "main", "tir.noalias": True}) # body # with T.block("root") for i0 in T.vectorized(256): # 这里使用了向量化命令 with T.block("C"): x = T.axis.spatial(256, i0) T.reads(A[x], B[x]) T.writes(C[x]) C[x] = A[x] + B[x]
调用向量指令后,打印的LLVM-IR如下,可以看到LLVM-IR按照你的要求将这个操作向量化了:
; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn
define internal fastcc void @main_compute_(i8* noalias nocapture align 128 %0, i8* noalias nocapture readonly align 128 %1, i8* noalias nocapture readonly align 128 %2) unnamed_addr #1 {
entry:
%3 = bitcast i8* %2 to <256 x float>*
%4 = load <256 x float>, <256 x float>* %3, align 128, !tbaa !113
%5 = bitcast i8* %1 to <256 x float>*
%6 = load <256 x float>, <256 x float>* %5, align 128, !tbaa !127
%7 = fadd <256 x float> %4, %6
%8 = bitcast i8* %0 to <256 x float>*
store <256 x float> %7, <256 x float>* %8, align 128, !tbaa !141
ret void
}
使用这个LLVM-IR最终生成的汇编代码,就会使用该CPU上的的向量指令,比如SSE、AVX2等等,相比原始的标量操作会快不少。
这只是其中一部分的细节,涉及到的知识已经有很多,比如LLVM、CPU指令集等等,想要好好深入的话,是需要花费一定的精力的。
对于AI编译器,我了解的也不是很深,不过通过这几年的尝鲜到常用,发现这些工具确实在某些场景下是有一定用处的。比如某些corner case,常见的卷积shape已经有很多人工写的kernel库去做(cudnn、cublas),但是不常见的卷积shape直接使用这些kernel库性能就差些了,这个时候就可以使用AI编译器设定shapes自动搜索,比自己写的话,人力和时间成本都要低很多。
当然AI编译器还有很多其他的用法,很多初创公司使用TVM作为自家硬件的编译器,通过修改中端、后端去适配自己的硬件,快速搭建一套适合自家硬件的AI编译器给客户使用。通过AI编译器生成的op算子,对于刚开始出生的硬件来说,可能比人工手写性能好的概率更高一些。
AI编译器,之前花了不少时间去研究,之后也会花些时间研究,不过也不是专职commit的,之后会尝试更多地向开源社区贡献下,写一些关于TVM的文章。
之前也有很多小伙伴想和我一起学TVM,但因为种种原因没有坚持下来(我也是),毕竟自身时间是有限,学习期间会被各种东西干扰,尤其是还有工作。其实学习这个东西大部分人不可能一直有大把时间投入的,我们只要坚持学就好,慢慢来,有时间就看看。
硬件底层
本科时我是搞嵌入式的,那会学51、学stm32,也参加一些乱七八槽的电子类比赛。后来研究生时期选择了深度学习方向,出于对硬件的兴趣,除了训练也会做一些工程类的项目,搞过的板卡有树莓派、jetson-TX2、FPGA等。
依稀记得本科那会,51是89C51、stm32是stm32f103zet6(或者c8t6),一个8位、一个32位。学习嵌入式对理解计算机原理有大的帮助,51或者stm32都是小型计算机了,只不过缺少运行os的必要单元,只能跑跑RTOS(ucos-ii这种的)。后来更高级点的树莓派系列就可以跑Ubuntu了。
抛开这些比较简单的单片机,现在我们使用的GPU,比如NVIDIA的显卡,结构就复杂不少,指令集更多、寄存器也更多,支持的功能也相应更多,想要实现极致的性能,就需要对芯片架构特别了解,通过intrinsics或者汇编实现应用的性能。不过做到这一点,门槛和难度大的不是一点半点。
不过所幸NVIDIA提供了很强大的编译器NVCC,提供了用于并行异构计算的编程模型CUDA (Compute Unified Device Architecture) ,也就是我们熟知的GPU编程。通过编写C++和C语言代码就可以实现性能很不错的CUDA代码,我们目前使用的CUDA代码大部分也不需要嵌入汇编,这与CUDA的优秀脱不了关系。
硬件是挺有意思的,各式各样的硬件,CPU、GPU、NPU、VPU、DSP,每个硬件架构设计也各不相用。比如CPU的向量化是SIMD,而GPU的多线程是SIMT,hexagon DSP则使用VLIW超长指令集去实现并行…
精通其中某一方向都是需要大把时间努力的,想要写出极致性能的算子就需要对使用的硬件特别的了解。不过现在比较好的是深度学习编译器的发展,自动生成的算子大概能满足80%的场景,很多场景不需要手动去写intrinsics或者汇编,利用生成的算子差不多能达到与纯手工优化媲美的性能。
硬件底层这个方向,个人感觉如果基础打好,很多知识是可以迁移的,最近也在抽空看一本《计算机体系结构——量化研究方法》的书。看的比较吃力,但是偶尔看看发现很多硬件都有相似的地方,比如GPU的一个warp32个thread的硬件线程,和DSP的一个cluster两个硬件线程有类似的地方。
手机这种移动端AI部署落地也是目前需求较高的一个方向,毕竟手机芯片也是年年要更新的,关于这方面要学习的有很多,算法SDK、算法移植、算法op优化、甚至具体到芯片层面,arm的指令、gpu、npu、dsp的优化等等要做的东西很多很多,
计算机原理、计算机组成原理、汇编原理、深入理解计算机原理…这些基础看看看完对于大部分的硬件都会有个直观的印象。
哎,要看的东西太多,慢慢看吧。
就业方向
很多小伙伴问我,老潘我该学什么啊,我这样做对不对啊,我学这个对以后找工作有没有帮助啊等等。我只能这个说看情况,面试要准备什么要问什么问题这个分公司,不同公司不同jd侧重点也不一样。而且这个很看个人,每个人喜欢的也不一样,比如说我喜欢的大概方向是部署,对于训练没有那么热衷,喜欢一些比较实际的东西。
所以也看个人的兴趣爱好,想做哪些点,比如部署的方向也有很多,如果不知道方向可以先把基础打好,C++基础、深度学习基础、计算机原理、编译原理、硬件什么的基础打好吧。可以先慢慢来,一般一开始也不清楚自己喜欢什么想要什么,还是要慢慢看才会明白自己到底对什么感兴趣。
之后在深入,可以根据自己的兴趣方向选择以下几个学习点:
- 对算法、特征、业务、实际算法场景感兴趣,可以专注深度学习各种算法知识(识别、检测、分类、校准、损失函数),然后基于这些知识解决实际的问题,可以训练模型,也可以结合传统图像或者其他方法一起解决。现实生活中的问题千奇百怪,一般只使用一个模型解决不了,可能会有多个模型一起上,解决方法也多种多样
- 对AI落地、部署、移植、算法SDK感兴趣,可以多看工程落地的一些知识(C++、Makefile、cmake、编译相关、SDK),多参与一些实际项目攒攒经验,多熟悉一些常见的大厂造的部署轮子(libtorch、TensorRT、openvino、TVM、openppl、Caffe等),尝试转几个模型(ONNX)、写几个op(主要是补充,性能不要最优)、写几个前后处理、debug下各种奇葩的错误,让模型可以顺利在一些平台跑起来,平台可以在PC、手机、服务器等等;
- 对算子优化、底层硬件实现实现感兴趣的,可以重点看某一平台的硬件架构(CPU、GPU、NPU、VPU、DSP),然后学学汇编语言,看看内存结构,看看优化方法等等,做一些算子的优化,写一些OP啥的,再者涉及到profile、算子优化、内存复用等等。
- 当然还有模型压缩、模型剪枝、量化、蒸馏,这些其实也是部署的一部分,根据需要掌握即可。
以上只是不完全概括,差不多是这个样子。很多情况下上述几个都可以参与都可以做,没有分那么绝对。去了公司你就是解决问题的人,来什么问题就解决什么问题,对于很多业务场景也不需要每一项都很牛逼,掌握80%就够用了。
另外,除了工程技术这些硬能力,个人感觉还需要一些软实力,随着我们慢慢地成长,有时候会发现技术并不能解决所有事,或者说靠自己一个人并不能完成一个比较大的任务。因此也需要考虑下这些点:
- 了解产品需求。如何将需求转化为实际的产品,咱们不能光知道开发,贴近实际的使用者是最好的,虽然在公司有PM来弄了,但是如果你可以以用户的角度来设计工具的话,或许会更好
- 和各种人打交道的能力,开发代码不是一个人的事儿,一个大的项目肯定是好几个人共同努力的结果,需要沟通联调各个部门,协商一些问题,合作开发代码等等
- 影响力,个人品牌,多写写文章、多帮助帮助他人、多参与一下开源、参与一下贡献,都是不错的
想躺平,还是走出舒适区,还得看你自己。
后记
偶然在知乎上看到程序员35岁这个梗,这个看到过好多次,对此目前也没有什么想法。能做的,也就是提前想想路子,同时学习学习。虽然未来五年、或者未来两年技术会发展成什么样,我们是无法预测的,但我们可以把自身的基础打好,然后再适应接下来的挑战。
加油吧!
看到这里了,感谢你听老潘的唠叨,你说我文章又臭又长也罢、废话也多也罢,总之我是把我的想法都写出来了,赞同的,点个赞,不赞同的,欢迎留言哦。
非常不错
写的很真实,对于一个不是专业计算机方向的我来说看后收益很多
非常之专业