BoT-SORT复现

源码:NirAharon/BoT-SORT.git

环境配置

pytorch在pytorch/previous-versions中下载。

conda create -n botsort_env python=3.7
conda activate botsort_env

git clone https://github.com/NirAharon/BoT-SORT.git
cd BoT-SORT
pip3 install -r requirements.txt
python3 setup.py develop

pip3 install cython; pip3 install 'git+https://github.com/cocodataset/cocoapi.git#subdirectory=PythonAPI'

# cuda=10.1 库要求pytorch>1.6
conda install pytorch==1.7.1 torchvision==0.8.2 torchaudio==0.7.2 cudatoolkit=10.1 -c pytorch

# Cython-bbox
pip3 install cython_bbox

# faiss cpu / gpu
pip3 install faiss-cpu
pip3 install faiss-gpu

安装过程中遇到

error: legacy-install-failure
 
× Encountered error while trying to install package.
╰─> lap
 
note: This is an issue with the package mentioned above, not pip.
hint: See above for output from the failure.

则直接用conda安装lap库:

conda install -c conda-forge lap

退出环境时若出现错误:

deactivate does not accept arguments
remainder_args: ['py37']

则直接使用命令:

conda deactivate

数据集准备

1.用wget指令在后台下载数据集,下载到space/datasets/

nohop wget https://motchallenge.net/data/MOT17.zip &
nohop wget https://motchallenge.net/data/MOT20.zip &
unzip MOT17.zip
unzip MOT20.zip # 解压

解压的时候遇到下面的问题,说明压缩的文件包不完整,需要重新下载。

Archive:  instantclient-basic-linux.x64-11.2.0.4.0.zip
End-of-central-directory signature not found.  Either this file is not
a zipfile, or it constitutes one disk of a multi-part archive.  In the
latter case the central directory and zipfile comment will be found on
the last disk(s) of this archive.

2.处理数据集,处理完的数据集放在BOT-SORT/fast-reid/datasets/MOT17_reid

cd space/BOT-SORT

# For MOT17 
python3 fast_reid/datasets/generate_mot_patches.py --data_path ../datasets --mot 17

# For MOT20
 python3 fast_reid/datasets/generate_mot_patches.py --data_path ../datasets --mot 20

Model Zoo

预训练模型MOT17-SBS-S50MOT17-SBS-S50, MOT20-SBS-S50MOT20-SBS-S50本地下载后上传到BOT-SORT/pretrained。(直接用wget云端下载速度太慢)

训练train

用MOT17/FRCNN-*/的前一半图像/帧训练reid;用MOT20前一半图像训练reid。结果放在./logs/MOT17/sbs-S50

cd <BoT_SORT-dir>
conda activate botsort_env
python3 fast_reid/tools/train_net.py --config-file ./fast_reid/configs/MOT17/sbs_S50.yml MODEL.DEVICE "cuda:0"

1.训练时出现错误:

TypeError: register_buffer()takes 3 positional arguments but 4 were given.

说明上一步预训练模型MOT17-SBS-S50没有下载,需要下载好放入pretrained路径下。

2.数据包找不到的问题

The current data structure is deprecated. 
Please ''put data folders such as "bounding_box_train" under ''"MOT17-ReID"

说明数据包没有按照dataloader要求的位置放置。根据’fast_reid/fastreid/data/datasets/mot17.py’和mot20.py,MOT17_ReID放在’fast_reid/datasets’下并改名为’MOT17-ReID’;MOT20_ReID放在’fast_reid/datasets/MOT20’下并改名为’MOT20-ReID’。

3.遇到与训练权重下载太慢的问题:

Downloading: "https://github.com/zhanghang1989/ResNeSt/releases/download/weights_step1/resnest50-528c19ca.pth" to /home/zhw/.cache/torch/hub/checkpoints/resnest50-528c19ca.pth

在本地下载好resnest50.pth后通过xftp上传到云端,再在xshell用mv语句移动文件到’.cache…'路径下:

mv /Users/Desktop/resnest50-528c19ca.pth /home/zhw/.cache/torch/hub/checkpoints

4.遇到训练未完成就总是中断的问题:

Connection closing...Socket close.
Connection closed by foreign host.

利用’fast_reid/fastreid/engine/defaults.py’的resume_or_load(resume=args.resume),令args.resume=True,接下来的训练能接着上一次的checkpoints继续训练。

parser.add_argument(
        "--resume",default=True,
        action="store_true",
        help="whether to attempt to resume from the checkpoint directory",
    )

5.'DefaultTrainer has no attribute ‘self.epoch’
未知,直接用原train_net.py运行,就没有该错误。

6.‘Address already in use’
用占用端口,没有权限杀死进程。
关闭xshell重新连接。

7.训练太慢:
使用单机多卡并行,利用多gpu同时训练

# For training MOT17
python3 fast_reid/tools/train_net.py --config-file ./fast_reid/configs/MOT17/sbs_S50.yml --num-gpus=2

# For training MOT20
python3 fast_reid/tools/train_net.py --config-file ./fast_reid/configs/MOT20/sbs_S50.yml --num-gpus=2

用3片gpu时常中断,用2片gpu最佳。

Track

1.MOT17
需要提前在ByteTrack下载bytetrack_x_mot17.pth.tar。
在tools/track.py文件修改–fast-reid-weights=r"logs/MOT17/sbs_S50/model_final.pth"和–fast-reid-config=r"logs/MOT17/sbs_S50/config.yaml"

-test: 用实时网络追踪test数据集

cd <BoT-SORT_dir>
python3 tools/track.py <dataets_dir/MOT17> --default-parameters --with-reid --benchmark "MOT17" --eval "test" --fp16 --fuse
python3 tools/interpolation.py --txt_path <path_to_track_result>
#实例化
python3 tools/track.py ../datasets/MOT17 --default-parameters --with-reid --benchmark "MOT17" --eval "test" --fp16 --fuse --fast-reid-config=r"logs/MOT17/sbs_S50/config.yaml" --fast-reid-weights=r"logs/MOT17/sbs_S50/model_final.pth"
python3 tools/interpolation.py --txt_path ./YOLOX_output/yolo_x_mix_det/track_results

#YOLOX-test
python3 tools/track_visdrone.py /home/sdc/zhuhuiwen_space/datasets/visdrone_mot/VisDrone2019-MOT-test-dev/sequences --default-parameters --benchmark "MOT17" --eval "val" --fp16 --fuse

<path_to_track_result>=./YOLOX_output/yolo_x_mix_det/track_results
<dataets_dir/MOT17>=…/datasets/MOT17

-val: 追踪val数据集,val为MOT/train中后一半帧/图像集。提前下载bytetrack_ablation.pth.tar。

cd <BoT-SORT_dir>
# BoT-SORT
python3 tools/track.py <dataets_dir/MOT17> --default-parameters --benchmark "MOT17" --eval "val" --fp16 --fuse
#实例化
python3 tools/track.py ../datasets/MOT17 --default-parameters --benchmark "MOT17" --eval "val" --fp16 --fuse
python3 tools/interpolation.py --txt_path ./YOLOX_outputs/yolo_x_ablation/track_results
# BoT-SORT-ReID
python3 tools/track.py ../datasets/MOT17 --default-parameters --with-reid --benchmark "MOT17" --eval "val" --fp16 --fuse

结果放在./YOLOX_output/yolo_x_ablation/track_results

2.MOT20
对于mot20同理,提前下载bytetrack_x_mot20.pth.tar放在pretrained下:

python3 tools/track.py ../datasets/MOT20 --default-parameters --with-reid --benchmark "MOT20" --eval "test" --fp16 --fuse --fast-reid-config=r"logs/MOT20/sbs_S50/config.yaml" --fast-reid-weights=r"logs/MOT20/sbs_S50/model_final.pth"
python3 tools/interpolation.py --txt_path ./YOLOX_output/yolo_x_mix_det/track_results

val:将tracker/GMC-file/MOTchallenge下的MOT20-01、02、03、05移动到tracker/GMC_file/MOT17_ablation

cd <BoT-SORT_dir>
# BoT-SORT
python3 tools/track.py ../datasets/MOT20 --default-parameters --benchmark "MOT20" --eval "val" --fp16 --fuse --fast-reid-config=r"logs/MOT20/sbs_S50/config.yaml" --fast-reid-weights=r"logs/MOT20/sbs_S50/model_final.pth"

# BoT-SORT-ReID
python3 tools/track.py ../datasets/MOT20 --default-parameters --with-reid --benchmark "MOT20" --eval "val" --fp16 --fuse --fast-reid-config=r"logs/MOT20/sbs_S50/config.yaml" --fast-reid-weights=r"logs/MOT20/sbs_S50/model_final.pth"

结果放在./YOLOX_output/yolo_x_mix_mot20_ch/track_results

Demo

先将图像集转化为视频,对视频追踪:

##########img2video.py#####################
import os
import cv2
import time

img_path = '../datasets/MOT17/test/MOT17-14-SDP/img1'
# 任意读取一张图片来代表视频中图片的尺寸
img = cv2.imread('../datasets/MOT17/test/MOT17-14-SDP/img1/000001.jpg')
imgInfo = img.shape
size = (imgInfo[1], imgInfo[0])
# 获得文件夹中图片的数量,从而进行循环生成视频文件
img_nums = len(os.listdir(img_path))
fourcc = cv2.VideoWriter_fourcc('M', 'P', '4', 'V') #.mp4
#fourcc = cv2.VideoWriter_fourcc('M', 'J', 'P', 'G') #.avi
# 写入对象 1.file name  2.编码器  3.帧率  4.尺寸大小
video_path='./YOLOX_outputs/yolox_x_mix_det/track_vis/'
videoWrite = cv2.VideoWriter(
    os.path.join(video_path,  'test_MOT17-14-SDP_video.mp4'), fourcc, 25, size)

# 读取这个文件夹中的每一张图片(按照顺序)然后组合成视频,帧率是每秒 25 帧
# 由于MOT20-05序列中,图片命名和其它序列不一样,此处借助以下代码来完成
for i in range(len(os.listdir(img_path))):
    print(i)
    if i < 9:
        filename = "00000" + str(i + 1) + ".jpg"
        filename = os.path.join(img_path, filename)
        # print(filename)
        img = cv2.imread(filename)
        videoWrite.write(img)

    elif 9 <= i < 99:
        filename = "0000" + str(i + 1) + ".jpg"
        filename = os.path.join(img_path, filename)
        # print(filename)
        img = cv2.imread(filename)
        videoWrite.write(img)

    elif 99 <= i < 999:
        filename = "000" + str(i + 1) + ".jpg"
        filename = os.path.join(img_path, filename)
        # print(filename)
        img = cv2.imread(filename)
        videoWrite.write(img)

    else:
        filename = "00" + str(i + 1) + ".jpg"
        filename = os.path.join(img_path, filename)
        # print(filename)
        img = cv2.imread(filename)
        videoWrite.write(img)

print("Process finish~")

将视频放在./YOLOX_output/yolo_x_ablation/track_vis,bytetrack_x_mot17.pth.tar在Bytetrack下载。

cd <BoT-SORT_dir>

# Original example
python3 tools/demo.py video --path <path_to_video> -f yolox/exps/example/mot/yolox_x_mix_det.py -c pretrained/bytetrack_x_mot17.pth.tar --with-reid --fuse-score --fp16 --fuse --save_result

python3 tools/demo.py image --path <path_to_video> -f yolox/exps/example/mot/yolox_x_mix_det.py -c pretrained/bytetrack_x_mot17.pth.tar --with-reid --fuse-score --fp16 --fuse --save_result

python3 tools/demo.py video --path /home/sdc/zhuhuiwen_space/BOT-SORT/YOLOX_outputs/yolox_x_mix_det/track_vis/test_MOT17-14-SDP_video.mp4 -f yolox/exps/example/mot/yolox_x_mix_det.py -c /home/sdc/zhuhuiwen_space/BOT-SORT/YOLOX_outputs/yolox_x_ablation/mix_mot_ch1/best_ckpt.pth.tar --with-reid --fuse-score --fp16 --fuse --save_result

python3 tools/demo_visdrone.py video --path /home/sdc/zhuhuiwen_space/BOT-SORT/YOLOX_outputs/yolox_x_visdrone/track_vis/uav0000009_03358_v.mp4 --fuse-score --fp16 --fuse --save_result

python3 tools/demo_mot20.py video --path ./YOLOX_outputs/yolox_x_mix_mot20_ch/track_vis/test_MOT20_08_video.mp4 -f yolox/exps/example/mot/yolox_x_mix_det.py -c pretrained/bytetrack_x_mot20.tar --with-reid --fuse-score --fp16 --fuse --save_result --fast-reid-config=r"logs/MOT20/sbs_S50/config.yaml" --fast-reid-weights=r"logs/MOT20/sbs_S50/model_final.pth"

# Multi-class example
python3 tools/mc_demo.py video --path <path_to_video> -f yolox/exps/example/mot/yolox_x_mix_det.py -c pretrained/bytetrack_x_mot17.pth.tar --with-reid --fuse-score --fp16 --fuse --save_result

<path_to_video>必须以.mp4结尾,此处<path_to_video>=./YOLOX_output/yolo_x_ablation/track_vis/test_MOT17-14-SDP_video.mp4
追踪视频放在./YOLOX_output/yolo_x_ablation/track_vis/当前时刻/test_MOT17-14-SDP_video.mp4

Eval

对val的追踪结果(./YOLOX_output/yolo_x_ablation/track_results/)对照ground truth进行评价。先对所有MOT17/train后一半图像的ground truth生成gt_val_half.txt,均放在MOT17/train/gt/

###################gen_gt_val_half.py###################
import os.path as osp
import os
import numpy as np
from tqdm import tqdm
import argparse
import os
import sys
import cv2

sys.path.append('.')

def mkdir_if_missing(d):
    if osp.isfile(d):
        d = osp.dirname(d)
    if not osp.exists(d):
        os.makedirs(d)

data_root = osp.join('../datasets') # the root directory of the dataset
gt_folder = osp.join(data_root, 'MOT17/train')
seqs_str = '''MOT17-02-DPM
              MOT17-04-DPM
              MOT17-05-DPM
              MOT17-09-DPM
              MOT17-10-DPM
              MOT17-11-DPM
              MOT17-13-DPM
              MOT17-02-FRCNN
              MOT17-04-FRCNN
              MOT17-05-FRCNN
              MOT17-09-FRCNN
              MOT17-10-FRCNN
              MOT17-11-FRCNN
              MOT17-13-FRCNN
              MOT17-02-SDP
              MOT17-04-SDP
              MOT17-05-SDP
              MOT17-09-SDP
              MOT17-10-SDP
              MOT17-11-SDP
              MOT17-13-SDP
              '''
seqs = [seq.strip() for seq in seqs_str.split()]

def gen_gt_val():
    for seq in tqdm(seqs):
        print('start seq {}'.format(seq))
        seq_info = open(osp.join(gt_folder, seq, 'seqinfo.ini')).read()
        seqLength = int(seq_info[seq_info.find('seqLength=') + 10:seq_info.find('\nimWidth')])
        print(seqLength)
        gt_txt = osp.join(gt_folder, seq, 'gt', 'gt.txt')
        gt = np.loadtxt(gt_txt, dtype=np.float64, delimiter=',')
        gt=sorted(gt, key = lambda x:x[0], reverse = False) ##########################

        save_val_gt = osp.join(gt_folder, seq, 'gt', 'gt_val_half.txt')
        val_start = seqLength // 2
        print(val_start)
        with open(save_val_gt, 'w') as f:
            for obj in gt:
                label_str = '{:d},{:d},{:d},{:d},{:d},{:d},{:d},{:d},{:.6f}\n'.format(
                    int(obj[0]), int(obj[1]), int(obj[2]), int(obj[3]), int(obj[4]), int(obj[5]), int(obj[6]), int(obj[7]), obj[8])
                if obj[0] > val_start+1:##########################################
                    f.write(label_str)

if __name__ == '__main__':
    gen_gt_val()

对MOT17追踪val的结果评价mota:

from loguru import logger

import argparse
import os
import sys
import os.path as osp
import cv2
import numpy as np

import torch
import torch.backends.cudnn as cudnn
from torch.nn.parallel import DistributedDataParallel as DDP

sys.path.append('.')

from yolox.core import launch
from yolox.exp import get_exp
from yolox.utils import configure_nccl, fuse_model, get_local_rank, get_model_info, setup_logger
#from yolox.evaluators import MOTEvaluator

import argparse
import os
import random
import warnings
import glob
import motmetrics as mm
from collections import OrderedDict
from pathlib import Path


def compare_dataframes(gts, ts):
    accs = []
    names = []
    for k, tsacc in ts.items():
        if k in gts:
            logger.info('Comparing {}...'.format(k))
            accs.append(mm.utils.compare_to_groundtruth(gts[k], tsacc, 'iou', distth=0.5))
            names.append(k)
        else:
            logger.warning('No ground truth for {}, skipping.'.format(k))

    return accs, names


# evaluate MOTA
results_folder = './YOLOX_outputs/yolox_x_ablation/track_results'
mm.lap.default_solver = 'lap'

gt_type = '_val_half'
#gt_type = ''
print('gt_type', gt_type)
gtfiles = glob.glob(
    os.path.join('../datasets/MOT17/train', '*/gt/gt{}.txt'.format(gt_type))) #'*/gt/gt{}.txt'
print('gt_files', gtfiles)
tsfiles = [f for f in glob.glob(os.path.join(results_folder, '*.txt')) if not os.path.basename(f).startswith('eval')]

logger.info('Found {} groundtruths and {} test files.'.format(len(gtfiles), len(tsfiles)))
logger.info('Available LAP solvers {}'.format(mm.lap.available_solvers))
logger.info('Default LAP solver \'{}\''.format(mm.lap.default_solver))
logger.info('Loading files.')

gt = OrderedDict([(Path(f).parts[-3], mm.io.loadtxt(f, fmt='mot15-2D', min_confidence=1)) for f in gtfiles])
ts = OrderedDict([(os.path.splitext(Path(f).parts[-1])[0], mm.io.loadtxt(f, fmt='mot15-2D', min_confidence=-1.0)) for f in tsfiles])
#os.path.splitext()是Python中的一个函数,用于将文件路径分割成文件名和扩展名两部分。

mh = mm.metrics.create()
accs, names = compare_dataframes(gt, ts)

logger.info('Running metrics')
metrics = ['recall', 'precision', 'num_unique_objects', 'mostly_tracked',
            'partially_tracked', 'mostly_lost', 'num_false_positives', 'num_misses',
            'num_switches', 'num_fragmentations', 'mota', 'motp', 'num_objects']
summary = mh.compute_many(accs, names=names, metrics=metrics, generate_overall=True)
# summary = mh.compute_many(accs, names=names, metrics=mm.metrics.motchallenge_metrics, generate_overall=True)
# print(mm.io.render_summary(
#   summary, formatters=mh.formatters,
#   namemap=mm.io.motchallenge_metric_names))
div_dict = {
    'num_objects': ['num_false_positives', 'num_misses', 'num_switches', 'num_fragmentations'],
    'num_unique_objects': ['mostly_tracked', 'partially_tracked', 'mostly_lost']}
for divisor in div_dict:
    for divided in div_dict[divisor]:
        summary[divided] = (summary[divided] / summary[divisor])
fmt = mh.formatters
change_fmt_list = ['num_false_positives', 'num_misses', 'num_switches', 'num_fragmentations', 'mostly_tracked',
                    'partially_tracked', 'mostly_lost']
for k in change_fmt_list:
    fmt[k] = fmt['mota']
print(mm.io.render_summary(summary, formatters=fmt, namemap=mm.io.motchallenge_metric_names))

metrics = mm.metrics.motchallenge_metrics + ['num_objects']
summary = mh.compute_many(accs, names=names, metrics=metrics, generate_overall=True)
print(mm.io.render_summary(summary, formatters=mh.formatters, namemap=mm.io.motchallenge_metric_names))
logger.info('Completed')

评价数据集用

python tools/gen_gt_val_half.py
python tools/mota_mot17.py

训练YOLOX模型

python3 yolox/train.py -f yolox/exps/example/mot/yolox_x_ablation.py -d 2 -b 4 --fp16 -o -c pretrained/yolox_x.pth
python3 tools/demo.py video --path /home/sdc/zhuhuiwen_space/BOT-SORT/YOLOX_outputs/yolox_x_mix_det/track_vis/test_MOT17-14-SDP_video.mp4 -f yolox/exps/example/mot/yolox_x_mix_det.py -c /home/sdc/zhuhuiwen_space/BOT-SORT/YOLOX_outputs/yolox_x_ablation/mix_mot_ch/best_ckpt.pth.tar --fuse-score --fp16 --fuse --save_result

python3 yolox/train.py -f yolox/exps/example/mot/yolox_x_visdrone.py -d 2 -b 4 --fp16 -o -c /home/sdc/zhuhuiwen_space/BOT-SORT/YOLOX_outputs/yolox_x_visdrone/epoch20_det/best_ckpt.pth.tar

python3 yolox/train.py -f yolox/exps/example/mot/yolox_x_visdrone.py -d 2 -b 4 --fp16 -o -c /home/sdc/zhuhuiwen_space/BOT-SORT/YOLOX_outputs/yolox_x_ablation/mix_mot_ch1/best_ckpt.pth.tar

python3 tools/track_visdrone.py /home/sdc/zhuhuiwen_space/datasets/visdrone_mot/VisDrone2019-MOT-test-dev/sequences --default-parameters --benchmark "MOT17" --eval "val" --fp16 --fuse
<think>好的,用户遇到了ModuleNotFoundError: No module named 'botsort'的问题,需要解决这个问题。首先,我应该先确认这个错误的原因。通常来说,ModuleNotFoundError意味着Python在当前的解释器环境中找不到对应的模块。可能的原因有几个: 1. **模块未安装**:这是最常见的情况,用户可能没有安装botsort这个包。需要检查是否已经正确安装。 2. **安装路径问题**:可能安装到了错误的Python环境,比如系统环境而不是虚拟环境,或者反之。 3. **包名称差异**:有时候PyPI上的包名称和导入时的名称不一致,需要确认安装时的名称是否正确。 接下来,我需要根据这些可能的原因给出解决方法。首先,用户应该尝试使用pip安装botsort。但需要确认包是否存在。搜索一下PyPI,发现botsort可能是一个第三方库,或者可能是某个项目的一部分,比如YOLO系列的跟踪算法中的BoT-SORT。如果是后者,可能需要从特定仓库安装。 例如,Ultralytics的YOLO可能集成了BoT-SORT,所以可能需要安装ultralytics包,或者单独安装botsort。但根据用户的引用内容,用户之前遇到了lap模块的问题,这通常和跟踪算法相关,比如线性分配问题。引用中提到需要先安装numpy,然后安装lap。这可能意味着botsort依赖这些包。 可能的解决步骤: 1. **安装botsort**:首先尝试直接安装,`pip install botsort`。如果失败,可能需要从GitHub仓库安装,比如`pip install git+https://github.com/...`。 2. **检查依赖项**:确保所有依赖库如numpy、lap已经安装。引用3提到安装lap前需要安装numpy,所以用户需要先安装numpy,再安装lap,最后安装botsort。 3. **虚拟环境确认**:用户可能在使用Anaconda环境,需要确保激活了正确的环境,并在该环境中安装这些包。 另外,用户引用的错误信息中有提到ultralytics包,可能botsort是ultralytics的一部分,或者需要安装特定版本的ultralytics。可能需要检查官方文档是否有相关说明。 总结步骤: - 确保在正确的Python环境中安装。 - 安装依赖项numpy和lap。 - 尝试安装botsort,如果失败,查找正确的安装源。</think>分步解释和解决方案: ### 问题原因分析 出现`ModuleNotFoundError: No module named 'botsort'`的主要原因是: 1. 未安装`botsort`模块 2. 模块安装路径未包含在Python系统路径中 3. 依赖包未正确安装(如`numpy`、`lap`等) ### 解决方法 #### 步骤1:确认Python环境 在终端中执行以下命令确认当前Python环境: ```bash python -c "import sys; print(sys.executable)" ``` 如果使用Anaconda,请确保已激活正确的虚拟环境: ```bash conda activate 环境名称 ``` #### 步骤2:安装核心依赖项 根据引用[3],必须确保`numpy`和`lap`已安装: ```bash pip install numpy pip install "lap>=0.4" ``` #### 步骤3:安装botsort模块 尝试通过以下方式安装: 1. **直接通过pip安装**: ```bash pip install botsort ``` 2. **若找不到包,尝试源码安装**(需要确认官方仓库地址): ```bash pip install git+https://github.com/NirAharon/BoT-SORT.git ``` 3. **如果与YOLOv8配合使用**,可能需要安装完整版ultralytics: ```bash pip install ultralytics[track] ``` #### 步骤4:验证安装 新建Python脚本执行导入验证: ```python from botsort import BOTSORT # 根据实际模块结构调整 print("导入成功") ``` ### 常见问题排查 1. **多Python版本冲突**:使用`python -m pip install`代替`pip install` 2. **权限问题**:在Linux/macOS中尝试添加`--user`参数 3. **代理问题**:若网络受限,添加`-i https://pypi.tuna.tsinghua.edu.cn/simple`使用国内镜像 ### 技术原理 Python的模块搜索路径通过`sys.path`控制,安装包时会将模块文件写入`site-packages`目录。当出现`ModuleNotFoundError`时,说明解释器在以下位置未找到模块文件: $$ \text{搜索路径} = \left[ \text{当前目录}, \text{PYTHONPATH}, \text{标准库路径}, \text{site-packages} \right] $$
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值