部署Azure Function加载.pkl模型时遇typing._ClassVar属性错误
问题:Azure Function部署后加载Azure ML训练的.pkl模型失败
在Windows本地VS Code中使用Python 3.8.6部署带有Azure ML训练的.pkl模型的Azure Function时,本地运行正常,但部署后调用joblib.load('model.pkl')加载模型失败,报错AttributeError: module 'typing' has no attribute '_ClassVar'。已尝试更换不同库、用pickle替代joblib加载模型,错误依旧。
1. requirements.txt
azure-functions joblib==0.14.1 numpyencoder==0.3.0 numpy==1.19.0 azureml-automl-runtime==1.49.0
2. 函数代码
import logging import azure.functions as func import os import json import pandas as pd from numpyencoder import NumpyEncoder def main(req: func.HttpRequest) -> func.HttpResponse: logging.info('Predict business classification') max_prediction_count = 3 logging.info('Predict business classification - before') model = joblib.load('model.pkl') logging.info('Predict business classification - after') company_name = req.params.get('name') limit = req.params.get('limit') max_predictions = max_prediction_count if limit: try: max_predictions = int(limit) except ValueError: max_predictions = max_prediction_count predictions = get_predictions(model, company_name) labels = model.classes_ res = predictions[0].tolist() res = [{'id': labels[i], 'confidence': x * 100} for i, x in enumerate(res)] res.sort(key=lambda d: d['confidence'], reverse=True) result = res[:max_predictions] return func.HttpResponse(body=json.dumps(result, cls=NumpyEncoder) , mimetype="application/json", status_code=200) def get_predictions(model, company_name): input_sample = pd.DataFrame(data=[{ "IN_CompanyNameClean": company_name, }]) predictions = model.predict_proba(input_sample) return predictions
3. 完整报错栈
Result: Failure Exception: AttributeError: module 'typing' has no attribute '_ClassVar' Stack: File "/azure-functions-host/workers/python/3.8/LINUX/X64/azure_functions_worker/dispatcher.py", line 452, in _handle__invocation_request call_result = await self._loop.run_in_executor( File "/usr/local/lib/python3.8/concurrent/futures/thread.py", line 57, in run result = self.fn(*self.args, **self.kwargs) File "/azure-functions-host/workers/python/3.8/LINUX/X64/azure_functions_worker/dispatcher.py", line 718, in _run_sync_func return ExtensionManager.get_sync_invocation_wrapper(context, File "/azure-functions-host/workers/python/3.8/LINUX/X64/azure_functions_worker/extension.py", line 215, in _raw_invocation_wrapper result = function(**args) File "/home/site/wwwroot/predictBusinessClassification/__init__.py", line 34, in main model = joblib.load('model.pkl') File "/home/site/wwwroot/.python_packages/lib/site-packages/joblib/numpy_pickle.py", line 605, in load obj = _unpickle(fobj, filename, mmap_mode) File "/home/site/wwwroot/.python_packages/lib/site-packages/joblib/numpy_pickle.py", line 529, in _unpickle obj = unpickler.load() File "/usr/local/lib/python3.8/pickle.py", line 1212, in load dispatch[key[0]](self) File "/home/site/wwwroot/.python_packages/lib/site-packages/joblib/numpy_pickle.py", line 342, in load_build Unpickler.load_build(self) File "/usr/local/lib/python3.8/pickle.py", line 1705, in load_build setstate(state) File "/home/site/wwwroot/.python_packages/lib/site-packages/azureml/automl/runtime/featurization/data_transformer.py", line 998, in __setstate__ new_data_transformer = DataTransformer() File "/home/site/wwwroot/.python_packages/lib/site-packages/azureml/automl/runtime/featurization/data_transformer.py", line 198, in __init__ from azureml.automl.runtime.sweeping.meta_sweeper import MetaSweeper File "/home/site/wwwroot/.python_packages/lib/site-packages/azureml/automl/runtime/sweeping/meta_sweeper.py", line 36, in <module> from ..scoring import Scorers, AbstractScorer File "/home/site/wwwroot/.python_packages/lib/site-packages/azureml/automl/runtime/scoring/__init__.py", line 5, in <module> from .abstract_scorer import AbstractScorer File "/home/site/wwwroot/.python_packages/lib/site-packages/azureml/automl/runtime/scoring/abstract_scorer.py", line 12, in <module> from azureml.automl.runtime.shared.metrics import is_better File "/home/site/wwwroot/.python_packages/lib/site-packages/azureml/automl/runtime/shared/metrics.py", line 18, in <module> from azureml.automl.runtime import _ml_engine File "/home/site/wwwroot/.python_packages/lib/site-packages/azureml/automl/runtime/_ml_engine/__init__.py", line 6, in <module> from .ml_engine import convert_to_onnx, featurize, validate, run_ensemble_selection File "/home/site/wwwroot/.python_packages/lib/site-packages/azureml/automl/runtime/_ml_engine/ml_engine.py", line 52, in <module> from azureml.automl.runtime._ml_engine.validation import AbstractRawExperimentDataValidator, \ File "/home/site/wwwroot/.python_packages/lib/site-packages/azureml/automl/runtime/_ml_engine/validation/__init__.py", line 7, in <module> from .featurization_config_data_validator import FeaturizationConfigDataValidator File "/home/site/wwwroot/.python_packages/lib/site-packages/azureml/automl/runtime/_ml_engine/validation/featurization_config_data_validator.py", line 19, in <module> from azureml.automl.runtime import _data_transformation_utilities File "/home/site/wwwroot/.python_packages/lib/site-packages/azureml/automl/runtime/_data_transformation_utilities.py", line 41, in <module> from azureml.core import Run File "/home/site/wwwroot/.python_packages/lib/site-packages/azureml/core/__init__.py", line 16, in <module> from .workspace import Workspace File "/home/site/wwwroot/.python_packages/lib/site-packages/azureml/core/workspace.py", line 22, in <module> from azureml._project import _commands File "/home/site/wwwroot/.python_packages/lib/site-packages/azureml/_project/_commands.py", line 23, in <module> from azureml._project.project_engine import ProjectEngineClient File "/home/site/wwwroot/.python_packages/lib/site-packages/azureml/_project/project_engine.py", line 12, in <module> import azureml._project.project_manager as project_manager File "/home/site/wwwroot/.python_packages/lib/site-packages/azureml/_project/project_manager.py", line 9, in <module> from azureml._project.ignore_file import AmlIgnoreFile File "/home/site/wwwroot/.python_packages/lib/site-packages/azureml/_project/ignore_file.py", line 6, in <module> import pathspec File "/home/site/wwwroot/.python_packages/lib/site-packages/pathspec/__init__.py", line 32, in <module> from .gitignore import ( File "/home/site/wwwroot/.python_packages/lib/site-packages/pathspec/gitignore.py", line 15, in <module> from .pathspec import ( File "/home/site/wwwroot/.python_packages/lib/site-packages/pathspec/pathspec.py", line 23, in <module> from . import util File "/home/site/wwwroot/.python_packages/lib/site-packages/pathspec/util.py", line 30, in <module> from .pattern import ( File "/home/site/wwwroot/.python_packages/lib/site-packages/pathspec/pattern.py", line 192, in <module> class RegexMatchResult(object): File "/home/site/wwwroot/.python_packages/lib/site-packages/dataclasses.py", line 950, in wrap return _process_class(cls, init, repr, eq, order, unsafe_hash, frozen) File "/home/site/wwwroot/.python_packages/lib/site-packages/dataclasses.py", line 800, in _process_class cls_fields = [_get_field(cls, name, type) File "/home/site/wwwroot/.python_packages/lib/site-packages/dataclasses.py", line 800, in <listcomp> cls_fields = [_get_field(cls, name, type) File "/home/site/wwwroot/.python_packages/lib/site-packages/dataclasses.py", line 659, in _get_field if (_is_classvar(a_type, typing) File "/home/site/wwwroot/.python_packages/lib/site-packages/dataclasses.py", line 550, in _is_classvar return type(a_type) is typing._ClassVar
解决方案
该错误根源是Python 3.8环境中安装了第三方dataclasses库,而Python 3.7+已内置dataclasses模块,第三方版本与内置版本冲突,导致typing._ClassVar的查找逻辑出错。
解决步骤
修正requirements.txt
修改依赖文件,强制排除第三方dataclasses(仅为Python 3.6及以下版本安装):azure-functions joblib==0.14.1 numpyencoder==0.3.0 numpy==1.19.0 azureml-automl-runtime==1.49.0 # 仅为Python 3.6及以下版本安装第三方dataclasses,3.8使用内置版本 dataclasses==0.6; python_version < "3.7"清理并重新部署
- 本地删除
.python_packages文件夹,清理旧依赖缓存 - 使用远程构建命令重新部署,确保环境安装正确依赖:
func azure functionapp publish <你的函数应用名称> --build remote
- 本地删除
额外优化
- 补上代码中缺失的
import joblib语句,避免运行时导入错误 - 将模型加载移到全局作用域,仅在函数启动时加载一次,提升请求响应速度:
import logging import azure.functions as func import os import json import pandas as pd from numpyencoder import NumpyEncoder import joblib # 补上缺失的导入 # 全局加载模型,仅启动时执行一次 model = joblib.load('model.pkl') max_prediction_count = 3 def main(req: func.HttpRequest) -> func.HttpResponse: logging.info('Predict business classification') company_name = req.params.get('name') limit = req.params.get('limit') max_predictions = max_prediction_count if limit: try: max_predictions = int(limit) except ValueError: max_predictions = max_prediction_count predictions = get_predictions(model, company_name) labels = model.classes_ res = predictions[0].tolist() res = [{'id': labels[i], 'confidence': x * 100} for i, x in enumerate(res)] res.sort(key=lambda d: d['confidence'], reverse=True) result = res[:max_predictions] return func.HttpResponse(body=json.dumps(result, cls=NumpyEncoder) , mimetype="application/json", status_code=200) def get_predictions(model, company_name): input_sample = pd.DataFrame(data=[{ "IN_CompanyNameClean": company_name, }]) predictions = model.predict_proba(input_sample) return predictions - 通过Azure门户的Kudu工具,确认
model.pkl已正确上传到函数应用的wwwroot目录
- 补上代码中缺失的
内容的提问来源于stack exchange,提问作者Ewa Łyko
相关产品推荐
相关产品推荐

