因paddlepaddle默认CPU的性能不及预期,为了提升性能,需要构建paddlepaddle的GPU环境。

环境信息

在高版本中,python最小的版本不能低于3.8,故需要python38的环境。

Python版本信息:

  • Python 3.8.10
GPU驱动需要提前安装,并且docker已经添加nvidia-container-runtime默认运行模式。

GPU驱动信息:

  • NVIDIA-SMI 470.82.01
  • Driver Version: 470.82.01
  • CUDA Version: 11.4

需要构建的版本为

模块名版本
paddlepaddle-gpu2.6.2
paddleocr2.8.1

操作步骤

编写Dockerfile

编辑Dockerfile文件,填写以下内容

FROM nvcr.io/nvidia/tritonserver:22.07-py3
MAINTAINER "lolicp.com"

ENV TZ=Asia/Shanghai DEBIAN_FRONTEND=noninteractive LD_LIBRARY_PATH=/usr/local/libs_ocr LANG="C.UTF-8"

RUN sed -i "s@http://.*archive.ubuntu.com@http://mirrors.tuna.tsinghua.edu.cn@g" /etc/apt/sources.list \
    && sed -i "s@http://.*security.ubuntu.com@http://mirrors.tuna.tsinghua.edu.cn@g" /etc/apt/sources.list

RUN apt-key del 7fa2af80 \
    && wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-keyring_1.0-1_all.deb \
    && dpkg -i cuda-keyring_1.0-1_all.deb \
    && apt update  \
    && apt install ffmpeg libsm6 libxext6 tmux htop tree libopencv-dev python3-opencv rapidjson-dev libgflags-dev libomp-dev -y

RUN wget https://github.com/Kitware/CMake/releases/download/v3.24.1/cmake-3.24.1-linux-x86_64.tar.gz  \
    && tar zxvf cmake-3.24.1-linux-x86_64.tar.gz && cp -r cmake-3.24.1-linux-x86_64/bin/* /usr/bin \
    && cp -r cmake-3.24.1-linux-x86_64/share/cmake-3.24 /usr/share \
    && rm cmake-3.24.1-linux-x86_64.tar.gz

RUN apt install build-essential lsof -y \
    && apt install libgl1-mesa-glx -y \
    && apt-get install libglib2.0-dev -y \
    && apt-get install ninja-build -y \
    && apt-get install python3.8 -y \
    && apt-get install python3-pip -y \
    && rm -rf /var/lib/apt/lists/*

RUN pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
RUN python3 -m pip install --upgrade pip && ln -s /usr/bin/pip3 /usr/bin/pip3.8

RUN python3 -m pip install \
    ipdb \
    shapely \
    pyyaml \
    pyclipper \
    scikit-image \
    imgaug==0.4.0 \
    lmdb \
    tqdm \
    numpy \
    python-Levenshtein

RUN python3 -m pip install  \
    opencv-python==4.4.0.46 opencv-contrib-python==4.4.0.46 opencv-python-headless==4.4.0.46 \
    cython \
    lxml \
    premailer \
    openpyxl \
    attrdict \
    tritonclient[all]

RUN python3 -m pip install \
    paddlepaddle-gpu==2.6.2

RUN python3 -m pip install \
    paddleocr==2.8.1 \
    loguru==0.7.0 \
    fastapi==0.95.1 \
    python-multipart

RUN mkdir -p  /root/.paddleocr/whl/det/ch/ch_PP-OCRv4_det_infer/ /root/.paddleocr/whl/rec/ch/ch_PP-OCRv4_rec_infer/ /root/.paddleocr/whl/cls/ch_ppocr_mobile_v2.0_cls_infer/ && wget https://paddleocr.bj.bcebos.com/PP-OCRv4/chinese/ch_PP-OCRv4_det_infer.tar -P /root/.paddleocr/whl/det/ch/ch_PP-OCRv4_det_infer/ && wget https://paddleocr.bj.bcebos.com/PP-OCRv4/chinese/ch_PP-OCRv4_rec_infer.tar -P /root/.paddleocr/whl/rec/ch/ch_PP-OCRv4_rec_infer/ && wget https://paddleocr.bj.bcebos.com/dygraph_v2.0/ch/ch_ppocr_mobile_v2.0_cls_infer.tar -P /root/.paddleocr/whl/cls/ch_ppocr_mobile_v2.0_cls_infer/

构建镜像

执行构建命令,耐心等待构建完成。

[root@lolicp ~]# docker build -t tritonserver:2.6.2 .

运行调试

查看驱动

通过命令启动容器,指定ID为1的GPU卡。

[root@lolicp ~]# docker run -it -e NVIDIA_VISIBLE_DEVICES=1 --name lolicp_com tritonserver:2.6.2 /bin/bash

=============================
== Triton Inference Server ==
=============================

NVIDIA Release 22.07 (build 41737377)
Triton Server Version 2.24.0

Copyright (c) 2018-2022, NVIDIA CORPORATION & AFFILIATES.  All rights reserved.

Various files include modifications (c) NVIDIA CORPORATION & AFFILIATES.  All rights reserved.

This container image and its contents are governed by the NVIDIA Deep Learning Container License.
By pulling and using the container, you accept the terms and conditions of this license:
https://developer.nvidia.com/ngc/nvidia-deep-learning-container-license

NOTE: CUDA Forward Compatibility mode ENABLED.
  Using CUDA 11.7 driver version 515.48.08 with kernel driver version 470.82.01.
  See https://docs.nvidia.com/deploy/cuda-compatibility/ for details.

root@c980d05203fd:/opt/tritonserver# nvidia-smi 
Fri Dec 27 20:00:16 2024       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 470.82.01    Driver Version: 470.82.01    CUDA Version: 11.7     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  Tesla T4            Off  | 00000000:00:11.0 Off |                    0 |
| N/A   66C    P0    19W /  70W |      0MiB / 15109MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

运行测试命令

root@f7309441c639:/opt/tritonserver# python3
Python 3.8.10 (default, Nov  7 2024, 13:10:47) 
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from paddleocr import PaddleOCR, draw_ocr
>>> ocr = PaddleOCR(use_angle_cls=True, lang='ch')
download https://paddleocr.bj.bcebos.com/PP-OCRv4/chinese/ch_PP-OCRv4_det_infer.tar to /root/.paddleocr/whl/det/ch/ch_PP-OCRv4_det_infer/ch_PP-OCRv4_det_infer.tar
[2024/12/27 20:22:32] ppocr INFO: Path /root/.paddleocr/whl/det/ch/ch_PP-OCRv4_det_infer/ch_PP-OCRv4_det_infer.tar already exists. Skipping...
download https://paddleocr.bj.bcebos.com/PP-OCRv4/chinese/ch_PP-OCRv4_rec_infer.tar to /root/.paddleocr/whl/rec/ch/ch_PP-OCRv4_rec_infer/ch_PP-OCRv4_rec_infer.tar
[2024/12/27 20:22:32] ppocr INFO: Path /root/.paddleocr/whl/rec/ch/ch_PP-OCRv4_rec_infer/ch_PP-OCRv4_rec_infer.tar already exists. Skipping...
download https://paddleocr.bj.bcebos.com/dygraph_v2.0/ch/ch_ppocr_mobile_v2.0_cls_infer.tar to /root/.paddleocr/whl/cls/ch_ppocr_mobile_v2.0_cls_infer/ch_ppocr_mobile_v2.0_cls_infer.tar
[2024/12/27 20:22:32] ppocr INFO: Path /root/.paddleocr/whl/cls/ch_ppocr_mobile_v2.0_cls_infer/ch_ppocr_mobile_v2.0_cls_infer.tar already exists. Skipping...
[2024/12/27 20:22:32] ppocr DEBUG: Namespace(alpha=1.0, alphacolor=(255, 255, 255), benchmark=False, beta=1.0, binarize=False, cls_batch_num=6, cls_image_shape='3, 48, 192', cls_model_dir='/root/.paddleocr/whl/cls/ch_ppocr_mobile_v2.0_cls_infer', cls_thresh=0.9, cpu_threads=10, crop_res_save_dir='./output', det=True, det_algorithm='DB', det_box_type='quad', det_db_box_thresh=0.6, det_db_score_mode='fast', det_db_thresh=0.3, det_db_unclip_ratio=1.5, det_east_cover_thresh=0.1, det_east_nms_thresh=0.2, det_east_score_thresh=0.8, det_limit_side_len=960, det_limit_type='max', det_model_dir='/root/.paddleocr/whl/det/ch/ch_PP-OCRv4_det_infer', det_pse_box_thresh=0.85, det_pse_min_area=16, det_pse_scale=1, det_pse_thresh=0, det_sast_nms_thresh=0.2, det_sast_score_thresh=0.5, draw_img_save_dir='./inference_results', drop_score=0.5, e2e_algorithm='PGNet', e2e_char_dict_path='./ppocr/utils/ic15_dict.txt', e2e_limit_side_len=768, e2e_limit_type='max', e2e_model_dir=None, e2e_pgnet_mode='fast', e2e_pgnet_score_thresh=0.5, e2e_pgnet_valid_set='totaltext', enable_mkldnn=False, fourier_degree=5, gpu_id=0, gpu_mem=500, help='==SUPPRESS==', image_dir=None, image_orientation=False, invert=False, ir_optim=True, kie_algorithm='LayoutXLM', label_list=['0', '180'], lang='ch', layout=True, layout_dict_path=None, layout_model_dir=None, layout_nms_threshold=0.5, layout_score_threshold=0.5, max_batch_size=10, max_text_length=25, merge_no_span_structure=True, min_subgraph_size=15, mode='structure', ocr=True, ocr_order_method=None, ocr_version='PP-OCRv4', output='./output', page_num=0, precision='fp32', process_id=0, re_model_dir=None, rec=True, rec_algorithm='SVTR_LCNet', rec_batch_num=6, rec_char_dict_path='/usr/local/lib/python3.8/dist-packages/paddleocr/ppocr/utils/ppocr_keys_v1.txt', rec_image_inverse=True, rec_image_shape='3, 48, 320', rec_model_dir='/root/.paddleocr/whl/rec/ch/ch_PP-OCRv4_rec_infer', recovery=False, return_word_box=False, save_crop_res=False, save_log_path='./log_output/', savefile=False, scales=[8, 16, 32], ser_dict_path='../train_data/XFUND/class_list_xfun.txt', ser_model_dir=None, show_log=True, sr_batch_num=1, sr_image_shape='3, 32, 128', sr_model_dir=None, structure_version='PP-StructureV2', table=True, table_algorithm='TableAttn', table_char_dict_path=None, table_max_len=488, table_model_dir=None, total_process_num=1, type='ocr', use_angle_cls=True, use_dilation=False, use_gpu=True, use_mlu=False, use_mp=False, use_npu=False, use_onnx=False, use_pdf2docx_api=False, use_pdserving=False, use_space_char=True, use_tensorrt=False, use_visual_backbone=True, use_xpu=False, vis_font_path='./doc/fonts/simfang.ttf', warmup=False)
>>> image_path='/opt/tritonserver/1442230498.jpg'
>>> result = ocr.ocr(image_path, cls=True)
[2024/12/27 20:23:20] ppocr DEBUG: dt_boxes num : 97, elapsed : 12.075563430786133
[2024/12/27 20:23:21] ppocr DEBUG: cls num  : 97, elapsed : 0.22832584381103516
[2024/12/27 20:23:21] ppocr DEBUG: rec_res num  : 97, elapsed : 0.47241711616516113
>>> result
[[[[[3.0, 51.0], [94.0, 50.0], [94.0, 64.0], [3.0, 66.0]], ('一表示:root/', 0.8774956464767456)], [[[12.0, 83.0], [127.0, 82.0], [127.0, 97.0], [12.0, 98.0]], ('新', 0.9949047565460205)], [[[134.0, 82.0], [224.0, 82.0], [224.0, 99.0], [134.0, 99.0]], ('新', 0.9877147674560547)], [[[319.0, 82.0], [447.0, 81.0], [447.0, 98.0], [320.0, 99.0]], ('Java', 0.9909888505935669)], [[[446.0, 83.0], [498.0, 83.0], [498.0, 99.0], [446.0, 99.0]], ('[Install]', 0.927994966506958)], [[[499.0, 82.0], [560.0, 82.0], [560.0, 99.0], [499.0, 99.0]], ('疝张楼能', 0.6521018743515015)], [[[8.0, 113.0], [125.0, 113.0], [125.0, 127.0], [8.0, 127.0]], ('選の', 0.6098349094390869)], [[[131.0, 112.0], [167.0, 109.0], [168.0, 124.0], [133.0, 126.0]], ('uE-', 0.5256553292274475)], [[[184.0, 111.0], [217.0, 111.0], [217.0, 126.0], [184.0, 126.0]], ('移動', 0.9901253581047058)], [[[219.0, 112.0], [250.0, 112.0], [250.0, 126.0], [219.0, 126.0]], ('削除', 0.9989633560180664)], [[[262.0, 112.0], [334.0, 112.0], [334.0, 126.0], [262.0, 126.0]], ('名前の变更', 0.9966350793838501)], [[[345.0, 112.0], [443.0, 112.0], [443.0, 126.0], [345.0, 126.0]], ('許可情報の变更', 0.9925376176834106)], [[[546.0, 112.0], [577.0, 112.0], [577.0, 126.0], [546.0, 126.0]], ('庄缩', 0.8648068904876709)], [[[581.0, 112.0], [627.0, 112.0], [627.0, 126.0], [581.0, 126.0]], ('Unzip', 0.9995032548904419)], [[[620.0, 110.0], [718.0, 110.0], [718.0, 127.0], [620.0, 127.0]], ('索', 0.9998088479042053)], [[[7.0, 155.0], [40.0, 155.0], [40.0, 175.0], [7.0, 175.0]], ('全', 0.9957225322723389)], [[[83.0, 155.0], [116.0, 155.0], [116.0, 175.0], [83.0, 175.0]], ('名前', 0.9849457740783691)], [[[436.0, 156.0], [482.0, 156.0], [482.0, 174.0], [436.0, 174.0]], ('所有者', 0.9936560988426208)], [[[586.0, 157.0], [646.0, 157.0], [646.0, 174.0], [586.0, 174.0]], ('許可情報', 0.8335392475128174)], [[[662.0, 157.0], [722.0, 157.0], [722.0, 174.0], [662.0, 174.0]], ('更新日時', 0.9051908850669861)], [[[85.0, 190.0], [117.0, 190.0], [117.0, 205.0], [85.0, 205.0]], ('上>', 0.7408500909805298)], [[[15.0, 220.0], [30.0, 220.0], [30.0, 234.0], [15.0, 234.0]], ('口', 0.8962425589561462)], [[[84.0, 218.0], [172.0, 220.0], [171.0, 241.0], [83.0, 238.0]], (' bash_ history.', 0.9305589199066162)], [[[360.0, 221.0], [372.0, 221.0], [372.0, 237.0], [360.0, 237.0]], ('2', 0.9996938705444336)], [[[434.0, 219.0], [566.0, 221.0], [566.0, 240.0], [434.0, 238.0]], ('lolicp.com hpusers', 0.983439564704895)], [[[586.0, 223.0], [644.0, 223.0], [644.0, 236.0], [586.0, 236.0]], ('rWxr-Xr-x', 0.883360743522644)], [[[661.0, 221.0], [739.0, 221.0], [739.0, 237.0], [661.0, 237.0]], ('Jun 6 10:50', 0.9686813950538635)], [[[15.0, 252.0], [30.0, 252.0], [30.0, 267.0], [15.0, 267.0]], ('口', 0.874782145023346)], [[[48.0, 250.0], [63.0, 250.0], [63.0, 265.0], [48.0, 265.0]], ('口', 0.5822321772575378)], [[[83.0, 253.0], [128.0, 253.0], [128.0, 271.0], [83.0, 271.0]], ('.config', 0.9272055625915527)], [[[359.0, 253.0], [372.0, 253.0], [372.0, 270.0], [359.0, 270.0]], ('3', 0.999495267868042)], [[[435.0, 252.0], [565.0, 254.0], [565.0, 272.0], [435.0, 270.0]], ('lolicp.com hpusers', 0.9909903407096863)], [[[586.0, 255.0], [616.0, 255.0], [616.0, 270.0], [586.0, 270.0]], ('rWx-', 0.9037610292434692)], [[[661.0, 253.0], [739.0, 253.0], [739.0, 270.0], [661.0, 270.0]], ('Jun 6 10:02', 0.9966550469398499)], [[[15.0, 284.0], [30.0, 284.0], [30.0, 299.0], [15.0, 299.0]], ('口', 0.874782145023346)], [[[83.0, 285.0], [162.0, 285.0], [162.0, 302.0], [83.0, 302.0]], ('-public _html', 0.8997741937637329)], [[[357.0, 286.0], [371.0, 284.0], [374.0, 300.0], [361.0, 303.0]], ('2', 0.9994038343429565)], [[[436.0, 285.0], [506.0, 285.0], [506.0, 302.0], [436.0, 302.0]], ('lolicp.com ', 0.9854088425636292)], [[[499.0, 287.0], [564.0, 287.0], [564.0, 301.0], [499.0, 301.0]], ('hpusers', 0.9991812705993652)], [[[586.0, 287.0], [643.0, 287.0], [643.0, 301.0], [586.0, 301.0]], ('rWX---r-X', 0.8345118165016174)], [[[661.0, 285.0], [738.0, 285.0], [738.0, 302.0], [661.0, 302.0]], ('Oct 9 22:02', 0.9943733811378479)], [[[14.0, 315.0], [31.0, 315.0], [31.0, 332.0], [14.0, 332.0]], ('口', 0.8949248194694519)], [[[83.0, 317.0], [113.0, 317.0], [113.0, 335.0], [83.0, 335.0]], ('.ssh', 0.8991168141365051)], [[[360.0, 318.0], [372.0, 318.0], [372.0, 333.0], [360.0, 333.0]], ('4', 0.9999070167541504)], [[[436.0, 318.0], [505.0, 318.0], [505.0, 334.0], [436.0, 334.0]], ('lolicp.com', 0.9985595941543579)], [[[507.0, 317.0], [564.0, 317.0], [564.0, 333.0], [507.0, 333.0]], ('hpusers', 0.9991369843482971)], [[[586.0, 319.0], [617.0, 319.0], [617.0, 333.0], [586.0, 333.0]], ('rWx--', 0.8305634260177612)], [[[661.0, 318.0], [739.0, 318.0], [739.0, 334.0], [661.0, 334.0]], ('Jun 6 10:49', 0.9858413338661194)], [[[14.0, 347.0], [31.0, 347.0], [31.0, 364.0], [14.0, 364.0]], ('口', 0.8949248194694519)], [[[82.0, 349.0], [129.0, 349.0], [129.0, 366.0], [82.0, 366.0]], ('Maildir', 0.9946942925453186)], [[[358.0, 349.0], [371.0, 347.0], [373.0, 365.0], [360.0, 367.0]], ('2', 0.999723494052887)], [[[436.0, 350.0], [504.0, 350.0], [504.0, 366.0], [436.0, 366.0]], ('lolicp.com', 0.9986031651496887)], [[[500.0, 350.0], [562.0, 350.0], [562.0, 366.0], [500.0, 366.0]], ('vchkpw', 0.9912247657775879)], [[[586.0, 351.0], [622.0, 351.0], [622.0, 365.0], [586.0, 365.0]], ('rWx---', 0.8442878127098083)], [[[661.0, 349.0], [738.0, 349.0], [738.0, 365.0], [661.0, 365.0]], ('Oct 9 03:05', 0.9046977758407593)], [[[15.0, 380.0], [30.0, 380.0], [30.0, 395.0], [15.0, 395.0]], ('口', 0.874782145023346)], [[[83.0, 379.0], [154.0, 381.0], [154.0, 400.0], [82.0, 398.0]], ('_db_dump', 0.9887813329696655)], [[[360.0, 381.0], [379.0, 381.0], [379.0, 399.0], [360.0, 399.0]], ('17', 0.9998593330383301)], [[[436.0, 380.0], [505.0, 382.0], [505.0, 400.0], [435.0, 397.0]], ('lolicp.com', 0.9958041310310364)], [[[507.0, 382.0], [565.0, 382.0], [565.0, 399.0], [507.0, 399.0]], ('hpusers', 0.9997890591621399)], [[[585.0, 382.0], [646.0, 382.0], [646.0, 399.0], [585.0, 399.0]], (' X-JX-JXMI', 0.8237661123275757)], [[[661.0, 381.0], [736.0, 381.0], [736.0, 398.0], [661.0, 398.0]], ('Oct 9 01:11', 0.9877657294273376)], [[[16.0, 412.0], [31.0, 412.0], [31.0, 426.0], [16.0, 426.0]], ('口', 0.9042556881904602)], [[[84.0, 412.0], [106.0, 415.0], [104.0, 431.0], [81.0, 428.0]], ('log', 0.9558840394020081)], [[[359.0, 413.0], [372.0, 413.0], [372.0, 430.0], [359.0, 430.0]], ('2', 0.9997151494026184)], [[[436.0, 413.0], [509.0, 413.0], [509.0, 430.0], [436.0, 430.0]], ('lolicp.com', 0.998340904712677)], [[[507.0, 413.0], [565.0, 413.0], [565.0, 430.0], [507.0, 430.0]], ('hpusers', 0.9998303651809692)], [[[586.0, 415.0], [644.0, 415.0], [644.0, 429.0], [586.0, 429.0]], ('rWxr-Xr-x', 0.8443599343299866)], [[[661.0, 413.0], [746.0, 413.0], [746.0, 430.0], [661.0, 430.0]], ('Dec 10 2023', 0.9878144860267639)], [[[17.0, 445.0], [30.0, 445.0], [30.0, 457.0], [17.0, 457.0]], ('√', 0.7239968776702881)], [[[84.0, 446.0], [158.0, 446.0], [158.0, 462.0], [84.0, 462.0]], ('public _html', 0.9252998232841492)], [[[359.0, 446.0], [372.0, 446.0], [372.0, 461.0], [359.0, 461.0]], ('3', 0.9994069337844849)], [[[435.0, 446.0], [465.0, 446.0], [465.0, 461.0], [435.0, 461.0]], ('root', 0.9990206956863403)], [[[511.0, 446.0], [540.0, 446.0], [540.0, 461.0], [511.0, 461.0]], ('root', 0.9990617036819458)], [[[586.0, 448.0], [644.0, 448.0], [644.0, 461.0], [586.0, 461.0]], ('rWxr-Xr-x', 0.8290747404098511)], [[[661.0, 445.0], [738.0, 445.0], [738.0, 461.0], [661.0, 461.0]], ('Oct 9 22:04', 0.9377818703651428)]]]

测试脚本

由python3.8运行

单个图片识别
from paddleocr import PaddleOCR, draw_ocr
ocr = PaddleOCR(use_angle_cls=True, lang='ch')
image_path='/opt/tritonserver/1442230498.jpg'
result = ocr.ocr(image_path, cls=True)
result
图片列表识别
from paddleocr import PaddleOCR, draw_ocr
ocr = PaddleOCR(use_angle_cls=True, lang='ch')

with open('/opt/tritonserver/image.txt', 'r') as file:
    image_paths = file.readlines()

for image_path in image_paths:
    image_path = image_path.strip()
    result = ocr.ocr(image_path, cls=True)
    result

参考文档: https://paddlepaddle.github.io/PaddleOCR/latest/quick_start.html

END

本文标题:docker构建paddlepaddle-gpu和paddleocr镜像环境

本文作者:宇宙最帅的男人

本文链接:https://lolicp.com/linux/202427731.html

版权声明:转载或者引用本文内容请注明来源及原作者,本文著作权归 (lolicp.com) 所有。

除非另有说明,本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议

最后修改:2024 年 12 月 28 日
如果觉得我的文章对你有用,请随意赞赏