Получение ModuleNotFoundError, когда модуль находится рядом с вызывающим файлом - PullRequest
0 голосов
/ 17 марта 2020

Я нахожусь в странной ситуации, когда для того, чтобы мой пакет вызывался нормально, один единственный файл (models.py) должен находиться рядом с вызывающим скриптом, а также внутри папки пакета, где он используется.

Чтобы было немного яснее, вот как выглядит организация пакета:

-FV_dir
 ---__init__.py
 ---F_V.py
 ---models.py
 ---utils.py
 ---service_utils.py
 ---subModule1_dir
    ----__init__.py
    ----detector.py
    ----utils.py
    ----subdir1
        --- etc
    ----subdir2
        --- etc
    ----subdir3

Весь этот пакет помещен в site-packages, поэтому его можно использовать для всей системы. И есть пользовательский скрипт, который использует этот пакет следующим образом: service_client.py:

from FV.service_utils import ServiceCore
from FV.utils import a_helper_function

def run():
  service = ServiceCore()
  service.run()

if __name__ == "__main__":
   run()

Сам ServiceCore использует F_V.py, который здесь является основным модулем. Сам модуль F_V использует models.py и utils.py рядом с ним следующим образом:

F_V.py:

from FV.utils import func1, func2  
from FV.models import model1, model2, model3
...

Теперь проблема в том, если models.py не рядом с клиентским кодом (service_client.py), он просто жалуется, что модуль не найден: вот пример ошибки, которую я получаю, когда это так:

           └─19146 /home/user1/anaconda3/bin/python3 /home/user1/Documents/service_client.py

Mar 17 19:22:54 ubuntu python3[19146]:     self.fv = FaceVerification(**cfg['Face_Verification']['ARGS'])
Mar 17 19:22:54 ubuntu python3[19146]:   File "/home/user1/anaconda3/lib/python3.7/site-packages/FV/F_V.py", line 58, in __init__
Mar 17 19:22:54 ubuntu python3[19146]:     self._init_model()
Mar 17 19:22:54 ubuntu python3[19146]:   File "/home/user1/anaconda3/lib/python3.7/site-packages/FV/F_V.py", line 80, in _init_model
Mar 17 19:22:54 ubuntu python3[19146]:     checkpoint = torch.load(self.model_checkpoint_path, map_location=torch.device('cpu'))
Mar 17 19:22:54 ubuntu python3[19146]:   File "/home/user1/anaconda3/lib/python3.7/site-packages/torch/serialization.py", line 529, in 
Mar 17 19:22:54 ubuntu python3[19146]:     return _legacy_load(opened_file, map_location, pickle_module, **pickle_load_args)
Mar 17 19:22:54 ubuntu python3[19146]:   File "/home/user1/anaconda3/lib/python3.7/site-packages/torch/serialization.py", line 702, in 
Mar 17 19:22:54 ubuntu python3[19146]:     result = unpickler.load()
Mar 17 19:22:54 ubuntu python3[19146]: ModuleNotFoundError: No module named 'models'

Если я удаляю модели. py рядом с the F_V.py, очевидно, F_V.py будет жаловаться, так как он непосредственно его использует:


           └─19216 /home/user1/anaconda3/bin/python3 /home/user1/Documents/fv_service_linux.py

Mar 17 19:27:33 ubuntu systemd[1532]: Started FV Service.
Mar 17 19:27:34 ubuntu python3[19216]: Traceback (most recent call last):
Mar 17 19:27:34 ubuntu python3[19216]:   File "/home/user1/Documents/fv_service_linux.py", line 83, in <module>
Mar 17 19:27:34 ubuntu python3[19216]:     from FV.service_utils import ServiceCore
Mar 17 19:27:34 ubuntu python3[19216]:   File "/home/user1/anaconda3/lib/python3.7/site-packages/FV/service_utils.py", line 21, in <mod
Mar 17 19:27:34 ubuntu python3[19216]:     from FV.F_V import FaceVerification
Mar 17 19:27:34 ubuntu python3[19216]:   File "/home/user1/anaconda3/lib/python3.7/site-packages/FV/F_V.py", line 17, in <module>
Mar 17 19:27:34 ubuntu python3[19216]:     from FV.models import resnet18, resnet50, resnet101
Mar 17 19:27:34 ubuntu python3[19216]: ModuleNotFoundError: No module named 'FV.models'

Так что единственный способ заставить это работать - это иметь models.py рядом с клиентским кодом. , Я не могу понять, почему это происходит, поскольку код клиента даже не взаимодействует напрямую с models.py. Что мне здесь не хватает?

Обновление:

В F_V.py я использую Pytorch и загружаю предварительно обученную модель. Я думал, что это не имеет отношения, и я делал что-то не так в отношении упаковки в Python, однако, оказывается, что это действительно был виновник. прочитайте ответ для получения дополнительной информации.

1 Ответ

0 голосов
/ 18 марта 2020

На самом деле это была проблема, связанная с Pytorch, в которой весь этот беспорядок был вызван этой простой командой при загрузке модели внутри F_V.py:

if self.device == 'cpu':
    checkpoint = torch.load(self.model_checkpoint_path, map_location=torch.device('cpu'))
else:
    checkpoint = torch.load(self.model_checkpoint_path)
# the culprit!
self.model = checkpoint['model'].module

Этот способ хранения и загрузка модели очень плохая, предварительно обученные модели изначально были обернуты nn.DataParallel, и человек, который их спас, сделал это так:

def save_checkpoint(epoch, epochs_since_improvement, model, metric_fc, optimizer, acc, is_best):
    print('saving checkpoint ...')
    state = {'epoch': epoch,
             'epochs_since_improvement': epochs_since_improvement,
             'acc': acc,
             'model': model,
             'metric_fc': metric_fc,
             'optimizer': optimizer}
    # filename = 'checkpoint_' + str(epoch) + '_' + str(loss) + '.tar'
    filename = 'checkpoint.tar'
    torch.save(state, filename)
    # If this checkpoint is the best so far, store a copy so it doesn't get overwritten by a worse checkpoint
    if is_best:
        torch.save(state, 'BEST_checkpoint.tar')

Как видите, он использовал всю модель ('model': model) в state_dict и сохраните это. Он должен был использовать state_dict().
Это плохо, поскольку одна и та же иерархия файлов / структуры dir / все должно сохраняться постоянно, везде, где будет использоваться эта модель.
и на нас повлияло это так же, как вы можете видеть. Все клиентские службы полагались на models.py и нуждались в том, чтобы он был рядом с ними, хотя они даже не использовали его.
Изначально я думал, что для решения этой проблемы нам нужно самим создать модели и затем загрузить весы вручную.

if self.model_name == 'r18':
    self.model = resnet18(pretrained=False, use_se=use_se)
elif self.model_name == 'r50':
    self.model = resnet50(pretrained=False, use_se=use_se)
elif self.model_name == 'r101':
    self.model = resnet101(pretrained=False, use_se=use_se)
else:
    raise Exception(f"Model name: '{self.model_name}' is not recognized.")

# load the model weights
self.model.load_state_dict(checkpoint['model'].module.state_dict())

Обратите внимание, что, поскольку модель изначально была моделью nn.DataParallel, для доступа к самой модели мы используем свойство .module, а затем используем модели state_dict() для инициализации модели и надеюсь, это решит проблему.
Однако, похоже, что это не так, и поскольку модель сохраняется таким образом, кажется, что таким способом избавиться от таких зависимостей невозможно. вместо этого преобразуйте вашу модель в сценарий факела, а затем сохраните модель.
Таким образом, вы можете избавиться от всех неприятностей.

Решение 1:

Попробуйте преобразовать вашу модель в torch script, а затем используйте это вместо:

def convert_model(model, input=torch.tensor(torch.rand(size=(1,3,112,112)))):
        model = torch.jit.trace(self.model, input)
        torch.jit.save(model,'/home/Rika/Documents/models/model.tjm')

, а затем загрузите эту версию вместо:

# load the model 
self.model = torch.jit.load('/home/Rika/Documents/models/model.tjm')

Решение 2:

просто сохраните модель state_dict () снова и используйте ее вместо этого: я сам в итоге сделал:

self.model = checkpoint['model'].module
# create the new checkpoint based on what you need 
torch.save({'state_dict' : self.model.state_dict(), 'use_se':True},
            '/home/Rika/Documents/BEST_checkpoint_r18_2.tar')

and started using the new checkpoint and so far everything has been good 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...