
1. 先搞清楚Atlas到底是什么1.1 Atlas 300V 24G的身份定位最近很多人在問“atlas部署yolo”和“atlas 300v 24g 是運(yùn)算加速卡嗎”其實這兩個問題指向的是同一個東西昇騰Atlas系列里的AI推理加速硬件。Atlas是華為昇騰計算平臺的產(chǎn)品線名稱覆蓋從訓(xùn)練到推理的整套硬件而300V 24G就是其中一款非常典型的邊緣推理卡。先直接回答那個高頻問題Atlas 300V 24G確實是一張運(yùn)算加速卡但它不是通用的GPU而是一張NPUNeural-network Processing Unit架構(gòu)的AI推理卡。它的核心定位是給深度學(xué)習(xí)模型的推理階段做加速而不是拿來跑訓(xùn)練。也就是說你不太適合拿它來從零訓(xùn)練一個YOLO模型但一旦模型訓(xùn)練好了部署到這張卡上做實時推理它的能效比和成本優(yōu)勢就比較明顯了。具體來說Atlas 300V 24G這塊卡重點關(guān)注這幾個參數(shù)24GB的顯存容量意味著它足夠容納比較大的模型和較高的輸入分辨率功耗和散熱設(shè)計上屬于邊緣端部署友好型算力方面主要針對INT8/FP16精度的推理做了專門優(yōu)化。相比純CPU推理它的吞吐量通常能提升十倍甚至更多相比同價位的GPU推理卡在特定模型和場景下功耗更低單路推理延遲也更穩(wěn)定。1.2 推理卡和訓(xùn)練卡到底差在哪很多剛開始接觸Atlas的朋友會拿它和GPU做類比這種思路對也不對。對的地方在于它們都是異構(gòu)加速設(shè)備都需要主機(jī)側(cè)通過PCIe接口調(diào)用不對的地方在于NPU的架構(gòu)設(shè)計目標(biāo)非常專一——它就是為算子執(zhí)行、張量計算、定點量化這些推理場景設(shè)計的不像GPU還兼顧圖形渲染、通用并行計算等各方面。訓(xùn)練卡和推理卡的核心區(qū)別在兩方面精度支持不同訓(xùn)練卡為了梯度回傳的精度主要跑FP32甚至FP64推理卡為了吞吐量重點優(yōu)化FP16、INT8尤其是INT8量化后的計算密度遠(yuǎn)高于FP32。帶寬和算力配比不同訓(xùn)練要反復(fù)讀寫大量中間結(jié)果所以對顯存帶寬要求極高推理階段更多是常駐模型權(quán)重、按序流過數(shù)據(jù)因此推理卡在計算單元和緩存的設(shè)計上更強(qiáng)調(diào)“低延遲高吞吐”而不是“瘋狂刷帶寬”。舉個例子我在實際測試中把同一個小型檢測模型分別放上Atlas 300V 24G和一塊通用GPU推理卡輸入相同視頻流Atlas的整卡功耗明顯更低單幀延遲抖動也更小。這不是說GPU不好而是說明硬件選型要匹配場景拿訓(xùn)練卡做推理就像用跑車?yán)浤芘艿屎统杀径疾焕硐搿?.3 誰適合選Atlas 300V 24G從我的經(jīng)驗看選擇Atlas 300V 24G的主要有這么幾類人第一類是做邊緣AI盒子、智慧城市、工業(yè)質(zhì)檢這類項目的開發(fā)者和集成商。這類場景有明確的物理空間和功耗約束一臺服務(wù)器或者一個小機(jī)箱要塞進(jìn)多路視頻分析能力Atlas這種單卡低功耗高密度的方案很合適。第二類是已在昇騰生態(tài)里做算法移植的團(tuán)隊比如使用MindSpore框架訓(xùn)練模型或者需要兼容昇騰平臺統(tǒng)一部署。第三類是純粹對NPU推理感興趣的技術(shù)愛好者想低成本體驗國產(chǎn)加速卡的能力邊界。不過如果你目前的主力框架是PyTorch、TensorFlow且項目完全跑在X86GPU的運(yùn)維體系里遷移到Atlas確實需要一些額外工作量主要是模型轉(zhuǎn)換和環(huán)境適配。這個后面實操部分我會詳細(xì)講。2. 軟硬件環(huán)境與工具鏈整理2.1 硬件側(cè)先確認(rèn)這些事拿到Atlas 300V 24G后安裝之前必須確認(rèn)主機(jī)側(cè)的兼容性。首先是物理接口這塊卡通常是標(biāo)準(zhǔn)PCIe 3.0 x16接口長度和常規(guī)顯卡相近機(jī)箱要預(yù)留足夠的空間和供電。某些服務(wù)器主板的PCIe插槽供電能力偏弱有條件最好外接輔助供電線纜避免高負(fù)載推理時出現(xiàn)掉卡或重啟。然后是主機(jī)CPU架構(gòu)和操作系統(tǒng)版本。Atlas的驅(qū)動和CANN工具鏈在x86架構(gòu)下支持最好arm架構(gòu)也有對應(yīng)版本但在安裝和編譯時容易踩坑非必要不建議用arm當(dāng)主力開發(fā)機(jī)。操作系統(tǒng)方面官方發(fā)布包主要適配Ubuntu、CentOS、openEuler等Linux發(fā)行版Windows環(huán)境基本不用考慮直接跑CANN官方也沒有完整支持。安裝前我做了一份檢查清單分享給大家參考確認(rèn)PCIe插槽類型和供電余量用lspci | grep -i process看設(shè)備識別情況確認(rèn)主板BIOS開啟Above 4G Decoding很多主板默認(rèn)關(guān)閉影響大顯存映射確認(rèn)操作系統(tǒng)是純凈的Linux環(huán)境避免裝過舊版本NVIDIA驅(qū)動造成沖突準(zhǔn)備好至少50GB的磁盤空間CANN工具鏈解壓安裝之后體積不小記錄主機(jī)IP和root權(quán)限部分驅(qū)動安裝步驟需要編譯內(nèi)核模塊2.2 軟件棧驅(qū)動、固件、CANN和推理運(yùn)行時Atlas軟件棧的分層邏輯需要先理解不然遇到報錯時根本不知道是哪個環(huán)節(jié)出了問題。從底往上依次是NPU驅(qū)動Driver、固件Firmware、CANN工具鏈、以及上層推理運(yùn)行時ACL。驅(qū)動和固件負(fù)責(zé)讓操作系統(tǒng)識別NPU設(shè)備并通過內(nèi)核模塊和底層接口訪問硬件。安裝順序一般是先裝固件再裝驅(qū)動或者用官方提供的一體化腳本同時完成。版本必須嚴(yán)格匹配驅(qū)動和固件的版本號不一致時設(shè)備無法正常工作而且排查起來非常痛苦。CANN工具鏈這是昇騰的計算架構(gòu)層類比CUDA在NVIDIA生態(tài)里的位置。CANN提供了算子庫、圖編譯引擎、推理運(yùn)行時等核心組件。我們常聽說的ATC工具就是CANN自帶的模型轉(zhuǎn)換工具而ACLAscend Computing Language則是面向開發(fā)者的推理編程接口。版本規(guī)劃建議CANN有一個大版本號比如CANN 7.0、8.0對應(yīng)的Driver和Firmware也有自己的版本。我踩過最大的坑是混用版本比如驅(qū)動是新版CANN是舊版結(jié)果NPU初始化動不動報錯。建議全部從昇騰社區(qū)官網(wǎng)下載同一個Release配套的組件包一次性安裝到位不要“混搭”。2.3 環(huán)境變量與設(shè)備驗證安裝完成后環(huán)境變量不會自動生效需要手動source一下CANN的set_env.sh。如果你用的是bash可以這樣配# 假設(shè)CANN安裝目錄為 /usr/local/Ascend source /usr/local/Ascend/ascend-toolkit/set_env.sh然后在~/.bashrc里追加這行避免每次登錄都要手動source。配置完之后用下面的命令驗證設(shè)備是否被正確識別npu-smi info如果輸出能看到設(shè)備型號、芯片溫度、顯存占用、版本號等說明驅(qū)動和控制面已經(jīng)正常。這一步確認(rèn)了再往下走后面的問題排查會省很多力氣。注意npu-smi info的輸出里Device Info區(qū)域能看到類似“Atlas 300V Pro”或者具體的芯片類型這里顯示的型號和后續(xù)ATC轉(zhuǎn)換時的--soc_version參數(shù)直接相關(guān)一定要記下來。3. YOLO模型部署的完整鏈路3.1 總體思路ONNX作為中間格式部署YOLO到Atlas 300V 24G繞不開的一個環(huán)節(jié)就是模型格式轉(zhuǎn)換。我的建議是不要想著直接從PyTorch導(dǎo)出到OM昇騰的模型格式而是走一條更穩(wěn)妥的鏈路PyTorch → ONNX → OM。原因有幾個首先PyTorch導(dǎo)出的ONNX是比較中立的中間格式Atlas的ATC工具對ONNX的支持最成熟其次ONNX可以方便地檢查模型結(jié)構(gòu)看到輸入輸出的張量維度甚至直接用Netron可視化排查問題直觀很多最后如果后續(xù)要在多個平臺間切換ONNX幾乎是通用的留著這個中間產(chǎn)物以后重轉(zhuǎn)成本低。在導(dǎo)出ONNX這一步有一個小細(xì)節(jié)值得注意——YOLO模型的輸出除了檢測框坐標(biāo)和置信度還可能有多個輸出頭。我用的YOLOv5為例導(dǎo)出時要明確指定輸出的張量組合或者選擇帶后處理的版本。為了在Atlas上靈活調(diào)整NMS閾值、置信度閾值我建議導(dǎo)出原生的三個輸出張量box坐標(biāo)、objectness、類別概率后處理放到推理代碼里用CPU完成這樣可控性最強(qiáng)。3.2 ATC模型轉(zhuǎn)換實操拿到ONNX文件后下一步就是用ATC工具把它轉(zhuǎn)成Atlas能直接運(yùn)行的OM模型。ATC命令看似參數(shù)不多但每一個都需要認(rèn)真核對。下面給出一個實際能跑的轉(zhuǎn)換示例atc --modelyolov5s.onnx \ --framework5 \ --outputyolov5s \ --soc_versionAscend310P3 \ --input_shapeimages:1,3,640,640 \ --output_typeFP16 \ --insert_op_confaipp.cfg逐個解釋一下這些參數(shù)--framework5固定表示ONNX格式數(shù)字不能寫錯寫錯會直接提示“framework not support”--output生成的OM文件前綴轉(zhuǎn)換成功后會得到y(tǒng)olov5s.om--soc_version芯片型號這個必須和實際硬件一致。Atlas 300V 24G的常見值是Ascend310P3但不同批次和不同細(xì)分型號可能有差異最準(zhǔn)確的來源是前面npu-smi info里的顯示或者查官方硬件規(guī)格表--input_shapeimages:1,3,640,640固定動態(tài)輸入為靜態(tài)形狀。這里的意思是batch為13通道640x640分辨率。如果你需要動態(tài)batch或者動態(tài)分辨率需要加動態(tài)維度的配置但推理性能會受影響我的建議是能用靜態(tài)就靜態(tài)--output_typeFP16讓模型以FP16精度執(zhí)行。大部分檢測模型在FP16下精度損失極小但推理速度會有明顯提升--insert_op_confaipp.cfgAIPPAI Preprocessing配置把圖像預(yù)處理操作縮放、歸一化、色域轉(zhuǎn)換下沉到硬件執(zhí)行3.3 AIPP配置怎么把預(yù)處理“塞進(jìn)”硬件AIPP是Atlas比較有特色的功能它可以在芯片內(nèi)部完成圖像預(yù)處理從而省掉主機(jī)側(cè)CPU做resize、歸一化的時間。做YOLO部署時圖像預(yù)處理是固定流程非常適合交給AIPP。下面這段aipp.cfg是我在YOLOv5部署中用到的配置aipp_op { aipp_mode: static input_format: RGB888_U8 src_image_size_w: 640 src_image_size_h: 640 crop: false resize: true resize_w: 640 resize_h: 640 csc_switch: true rbuv_swap_switch: false min_chn_0: 0 min_chn_1: 0 min_chn_2: 0 var_reci_chn_0: 0.00392156862745098 var_reci_chn_1: 0.00392156862745098 var_reci_chn_2: 0.00392156862745098 }這里的邏輯是把原始RGB圖像統(tǒng)一縮放成640x640然后做歸一化乘以1/255也就是0.00392…。注意YOLOv5的預(yù)處理是除以255不需要減均值所以min_chn全部設(shè)為0。如果你的模型是在其他預(yù)處理邏輯下訓(xùn)練的比如ImageNet式的均值和方差這里就要對應(yīng)修改不然推理精度會崩得一塌糊涂。提醒一下AIPP模式可以選擇static和dynamic。static模式下模型固定接收src_image_size_w/src_image_size_h指定的尺寸dynamic模式允許運(yùn)行時指定尺寸但會占用額外資源。我的經(jīng)驗是能靜態(tài)就靜態(tài)避免動態(tài)帶來的額外開銷和潛在兼容問題。3.4 推理代碼編寫模型轉(zhuǎn)好之后推理代碼就用CANN的ACL接口來寫。ACL接口可以用C也可以用Python考慮到大多數(shù)做算法驗證和業(yè)務(wù)集成的朋友更熟悉Python我直接給一段Python風(fēng)格的推理偽碼核心流程如下初始化ACL和指定設(shè)備加載OM模型準(zhǔn)備輸入輸出內(nèi)存把輸入圖像數(shù)據(jù)拷貝到設(shè)備側(cè)執(zhí)行模型推理把輸出數(shù)據(jù)拷回主機(jī)側(cè)做后處理NMS、坐標(biāo)映射關(guān)鍵步驟簡化示例import acl # 初始化 ret acl.init() ret acl.rt.set_device(0) # 加載模型 model_path b./yolov5s.om model_id, ret acl.mdl.load_from_file(model_path) # 準(zhǔn)備輸入輸出 input_desc acl.mdl.create_dataset() output_desc acl.mdl.create_dataset() # 執(zhí)行推理 ret acl.mdl.execute(model_id, input_desc, output_desc) # 釋放資源 acl.mdl.unload(model_id) acl.rt.reset_device(0) acl.finalize()這里省略了數(shù)據(jù)搬運(yùn)和內(nèi)存管理的細(xì)節(jié)因為完整展開能寫好幾篇。核心點在于理解ACL的“dataset”邏輯輸入數(shù)據(jù)和輸出結(jié)果都掛在dataset里執(zhí)行時一次性傳給硬件。這個模型和CUDA的stream有點像但具體API差異不小習(xí)慣用GPU的朋友剛開始需要適應(yīng)一下。想快速跑通驗證的朋友可以用CANN自帶的樣例代碼作為起點昇騰官方倉庫里提供圖像分類和目標(biāo)檢測的Python樣例把模型路徑換掉就能跑起來。我的建議是先跑通官方樣例確認(rèn)環(huán)境和鏈路OK再改造成自己的業(yè)務(wù)代碼這樣排錯范圍小很多。3.5 后處理NMS和坐標(biāo)解碼YOLO模型的輸出是原始預(yù)測張量不能直接拿來畫框。你需要先在代碼里完成解碼把中心點坐標(biāo)轉(zhuǎn)成左上角和右下角坐標(biāo)把寬度高度從網(wǎng)格尺寸映射回原始圖像尺寸對objectness和類別概率做過濾。接著做NMS非極大值抑制去掉重疊的框。NMS這部分我建議直接用PyTorch或NumPy實現(xiàn)不用上復(fù)雜框架。性能上單幀640x640輸入后處理也就幾毫秒對整體吞吐影響有限。但如果你追求極致幀率比如并發(fā)處理多路視頻流NMS可以改用C算子實現(xiàn)或者用多進(jìn)程并行處理。這些優(yōu)化屬于后期調(diào)優(yōu)范疇第一版先把功能跑通更重要。4. 性能調(diào)優(yōu)與項目落地4.1 關(guān)鍵性能指標(biāo)延遲、吞吐、功耗評估一張推理卡的性能不能只看跑分要結(jié)合自己的業(yè)務(wù)來看。我一般關(guān)注三個指標(biāo)單幀延遲latency、吞吐量throughput、能效比性能和功耗的比值。單幀延遲直接影響實時性敏感的業(yè)務(wù)比如安防監(jiān)控里的行為識別延遲高了就沒意義。吞吐量影響的是批量任務(wù)的處理速度比如離線分析一整天錄制的視頻。Atlas 300V 24G在這種場景下優(yōu)勢明顯24GB顯存可以讓很多模型一次性駐留不需要頻繁換入換出。我用YOLOv5s、640x640輸入、FP16精度做了一套簡單測試在單卡上跑了循環(huán)推理CPU占用較低顯存占用約2~3GB整體吞吐能達(dá)到幾百幀每秒的量級具體數(shù)值和CPU性能、驅(qū)動版本、輸入來源都有關(guān)僅供參考。如果用INT8量化模型吞吐還能再上一個臺階但精度需要做驗證。4.2 合理設(shè)置Batch Size提升效率有些朋友在部署時習(xí)慣batch設(shè)為1這樣做沒問題但在批量推理場景下很浪費(fèi)算力。如果你的輸入是離線視頻文件集或者并發(fā)請求本來就比較多可以適當(dāng)加大batch。Atlas上的推理批次增加之后計算密度更高顯存帶寬利用率也更充分。我做一個簡單的換算幫助你理解假設(shè)單幀推理batch1時耗時15ms那么1秒大約能處理66幀。如果batch4時總耗時40ms那么1秒大約能處理100幀。也就是說雖然單batch的耗時變長了但總吞吐反而提升了50%。這并不絕對因為batch太大時也可能遇到內(nèi)存帶寬瓶頸所以需要實測不同batch下的延遲和吞吐找到拐點。4.3 多路視頻流的調(diào)度策略Atlas 300V 24G這類推理卡經(jīng)常被用于多路視頻流分析場景。假設(shè)你有16路攝像頭每路25fps那每秒總共需要處理400幀。此時單幀處理時間必須控制在2.5ms以內(nèi)這很考驗整體方案的調(diào)度能力。我的建議方案是用隊列解耦視頻解碼和推理。視頻解碼由CPUFFmpeg處理解碼后的幀先放入隊列推理進(jìn)程從隊列取幀送入Atlas執(zhí)行。多個視頻流共用一個推理線程批量推理時把多路幀拼成一個batch可以最大化利用硬件算力。后處理階段再按視頻流ID把結(jié)果分揀回去這樣代碼結(jié)構(gòu)清晰性能也可控。4.4 工程化注意事項日志、熱部署與監(jiān)控部署上線不是模型轉(zhuǎn)換完就結(jié)束了。在工程化落地階段至少要做好三件事日志、模型熱切換、狀態(tài)監(jiān)控。日志方面除了業(yè)務(wù)日志還要記錄推理的耗時指標(biāo)P50、P95、P99方便后續(xù)評估硬件是否老化或驅(qū)動是否異常。模型熱切換指的是能在不重啟服務(wù)的情況下更新模型ACL支持加載多個模型可以在業(yè)務(wù)代碼里維護(hù)版本號新模型加載成功后原子切換這樣升級不會中斷服務(wù)。狀態(tài)監(jiān)控方面用npu-smi info周期性采集芯片溫度、顯存和算力利用率設(shè)置告警閾值防患于未然。我在實際項目中還養(yǎng)成了一個習(xí)慣每跑一次版本更新就把當(dāng)前驅(qū)動版本、CANN版本、模型轉(zhuǎn)換參數(shù)、實測精度和性能數(shù)據(jù)記錄到一個文檔里。因為昇騰的版本更新比較頻繁有時候問題不是代碼寫的而是版本兼容引起的有歷史記錄就能快速定位。5. 常見問題與排查技巧實錄5.1 設(shè)備側(cè)問題問題1npu-smi info找不到設(shè)備這種情況我遇到過兩次一次是驅(qū)動沒裝好一次是版本不匹配。處理方法先檢查內(nèi)核模塊是否加載執(zhí)行l(wèi)smod | grep drv如果模塊正常再核對固件版本是否匹配驅(qū)動要求。如果用的是全新系統(tǒng)建議卸載后按官方文檔順序重裝固件 → 驅(qū)動 → 重啟 → 驗證。問題2推理時設(shè)備掉線或者報錯“Device 0 is not available”多半是供電不足或者溫度過高。檢查PCIe輔助供電是否接好、機(jī)箱風(fēng)道是否通暢用npu-smi info觀察溫度曲線。如果溫度接近上限降低輸入分辨率和并發(fā)路數(shù)試試實在不行就得加散熱措施。5.2 模型轉(zhuǎn)換與推理側(cè)問題問題1ATC轉(zhuǎn)換報“Unsupported op”這個問題在做YOLO系列部署時比較常見。某些PyTorch算子導(dǎo)出到ONNX后Atlas的算子庫還沒有覆蓋。解決思路是回到PyTorch側(cè)把對應(yīng)算子用等價的基礎(chǔ)算子替換再重新導(dǎo)出ONNX。比如某些版本的自適應(yīng)池化、特殊的激活函數(shù)都可能需要手工替換。問題2轉(zhuǎn)換成功但推理結(jié)果全是0或亂碼這種情況先檢查輸出張量的shape對不對再看看AIPP配置里的顏色通道順序是否匹配。我之前有一次轉(zhuǎn)好的模型推理結(jié)果一團(tuán)糟排查了半天發(fā)現(xiàn)是AIPP里把BGR和RGB順序搞反了導(dǎo)致彩圖變成灰度亂色改成正確的通道順序后立刻恢復(fù)正常。問題3推理速度比預(yù)期慢先看是不是模型成了FP32精度FP16和INT8的推理速度會有明顯差異再檢查輸入形狀是否和模型期望一致動態(tài)shape比靜態(tài)shape慢很多最后檢查代碼里是否大量使用同步等待數(shù)據(jù)拷貝和設(shè)備執(zhí)行是否重疊起來了。5.3 問題速查表現(xiàn)象可能原因處理建議nps-smi找不到設(shè)備驅(qū)動未裝好、固件版本不匹配按官方順序重裝固件和驅(qū)動ATC轉(zhuǎn)換報不支持的算子PyTorch算子導(dǎo)出后不被支持用基礎(chǔ)算子替換重新導(dǎo)出ONNX推理結(jié)果異常AIPP通道順序錯誤、歸一化參數(shù)錯誤核對顏色順序和歸一化系數(shù)推理速度慢模型精度未用FP16/INT8、動態(tài)shape轉(zhuǎn)成靜態(tài)shape用低精度推理顯存占用過高batch過大、模型過大降低batch或優(yōu)化模型結(jié)構(gòu)設(shè)備溫度偏高散熱差、高負(fù)載長時間運(yùn)行檢查風(fēng)道、降低并發(fā)或加散熱5.4 獨家避坑心得最后分享幾個不容易在文檔里看到、但實踐中確實會踩的坑。第一轉(zhuǎn)換模型時最好把--output_type明確設(shè)為FP16。有些版本默認(rèn)是FP32雖然精度高但推理速度只有FP16的一半不到白白浪費(fèi)硬件能力。第二不要迷信官方樣例的“一鍵推理”。官方樣例為了通用性往往做了大量抽象對于業(yè)務(wù)來說有些操作是多余的。我用過一段官方Python樣例發(fā)現(xiàn)它在每次推理時都重新分配輸入輸出內(nèi)存這在長期運(yùn)行時會造成不必要的內(nèi)存碎片。建議自己寫一個簡單內(nèi)存池用完復(fù)用。第三在模型轉(zhuǎn)換階段就要確定好預(yù)處理策略。AIPP后主機(jī)側(cè)不再需要做resize和歸一化。如果后續(xù)改了預(yù)處理邏輯而忘記重新生成OM模型推理結(jié)果一定是不對的而且這種問題很難發(fā)現(xiàn)因為數(shù)值只是“有一點錯”看起來像精度問題其實是預(yù)處理不匹配。第四也是最重要的一個經(jīng)驗先把官方環(huán)境驗證通過再動自己的業(yè)務(wù)代碼。我從入門到踩坑積累的最深教訓(xùn)是環(huán)境問題不解決所有調(diào)試都是浪費(fèi)時間的。先保證npu-smi info正常、官方樣例能跑通再逐步引入自己的模型和代碼你會少走很多彎路。從我個人的體會看Atlas 300V 24G是一張很值得認(rèn)真研究的推理加速卡它的性能下限和上限差距很大關(guān)鍵在于工具鏈選型和模型轉(zhuǎn)換的細(xì)節(jié)。只要把ONNX導(dǎo)出、ATC轉(zhuǎn)換、AIPP配置這幾個核心環(huán)節(jié)做扎實部署YOLO模型比想象中要順利。后續(xù)有空的話我打算再把INT8量化校準(zhǔn)和C性能優(yōu)化單獨寫一篇歡迎對NPU推理感興趣的朋友一起交流。