通过 GitHub Actions 实现简单模型重新训练自动化

张开发
2026/4/10 2:10:22 15 分钟阅读

分享文章

通过 GitHub Actions 实现简单模型重新训练自动化
原文towardsdatascience.com/simple-model-retraining-automation-via-github-actions-b0f61d5c869c机器学习模型可以为业务创造巨大的价值。然而开发它们不是一次性的活动。相反这是一个持续的过程以便模型能够持续提供价值。这就是 MLOps 的来源。将 CI/CD 原则与机器学习开发相结合我们称之为 MLOps其目的是通过模型提供持续的价值。机器学习模型带来持续收益的一种方式是在必要时重新训练它们例如如果检测到数据漂移。我们可以通过设置重新训练触发器来执行模型重新训练自动化。我们可以使用 GitHub 的 GitHub Actions 工具来简化重新训练过程。这个工具是 GitHub 为 CI/CD 平台提供的一个功能用于自动化从 GitHub 仓库开始的软件开发过程。本文将教会我们如何通过 GitHub Actions 执行受控的模型重新训练自动化。如何做到这一点让我们深入了解。准备工作我们将为这个项目进行简单的模型开发和自动化演示。整体项目结构将如下图表所示。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/c9b004fc42958fa6c14fda3c71d2cec0.png图片由作者提供让我们从准备 GitHub 仓库开始以便在这个仓库中使用 GitHub Actions。你可以创建一个名为你喜欢的空仓库。对我来说我创建了这个仓库。此外我们还会使用 Docker 模拟模型部署。为此让我们安装Docker Desktop。如果你还没有这样做请注册Dockerhub。然后让我们创建 GitHub 个人访问令牌PAT具有仓库和工作流程权限。将令牌放在安全的地方然后回到你刚刚创建的空仓库。转到设置并选择“密钥和变量”。接下来创建包含你的 PAT、Docker 用户名和 Docker 密码的仓库密钥。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/1614433a009a08861234ea4fd90a4240.png图片由作者提供将 GitHub 仓库拉取到你的本地或任何你正在工作的平台上。一旦准备就绪让我们准备教程的整体结构。在你的 IDE 中创建以下所示的文件夹。diabetes-project/├── data/├── notebooks/├── scripts/├── models/├──.github/│ └── workflows/|─────────────当你的文件夹就绪时我们会设置虚拟环境。这是一个最佳实践因为我们希望有一个隔离的环境。转到你的根目录在 CLI 中使用以下代码。python-m venv your_environment_name然后你可以通过运行以下代码来激活虚拟环境。your_environment_nameScriptsactivate在激活虚拟环境后我们将安装教程所需的全部必要包。在您的根目录中创建名为requirements.txt的文件并填写以下包。fastapi uvicorn pandas scikit-learn matplotlib seaborn evidently当requirement.txt文件准备好时我们将安装虚拟环境中的所有必需包。pip install-r requirements.txt所有的准备工作都已经就绪现在我们可以继续开发模型并启动模型重训练自动化。模型开发本教程将使用开源糖尿病数据集 (CC0: 公共领域)作为我们的示例数据集。请下载数据集并将其放入Data文件夹中。对我来说我将数据集重命名为data.csv但你可以将其更改为你喜欢的名称。我们将在 Jupyter Notebook 中执行初始模型开发。创建你的笔记本并将其放入notebooks文件夹中。然后让我们开始读取数据集。importpandasaspdimportseabornassnsimportmatplotlib.pyplotasplt data_path..//data//data.csvdfpd.read_csv(data_path)我们将关注除了数据探索之外的其他内容因为本文的重点是展示 GitHub Actions 的重训练自动化能力。如果您想查看数据探索部分我已经将其包含在笔记本中。现在让我们进入数据预处理和管道初始化。我们将使用数据管道来模拟标准开发流程。fromsklearn.model_selectionimporttrain_test_splitfromsklearn.pipelineimportPipelinefromsklearn.composeimportColumnTransformerfromsklearn.imputeimportSimpleImputerfromsklearn.preprocessingimportStandardScaler Xdf.drop(Outcome,axis1)ydf[Outcome]X_train,X_test,y_train,y_testtrain_test_split(X,y,test_size0.2,random_state42)numeric_featuresX.columns numeric_transformerPipeline(steps[(imputer,SimpleImputer(strategymean)),(scaler,StandardScaler())])preprocessorColumnTransformer(transformers[(num,numeric_transformer,numeric_features)])一旦管道准备就绪我们将使用随机森林算法作为我们的机器学习模型。您可以选择任何其他满足您需求的模型。fromsklearn.ensembleimportRandomForestClassifier pipelinePipeline(steps[(preprocessor,preprocessor),(classifier,RandomForestClassifier(random_state42))])pipeline.fit(X_train,y_train)让我们评估模型并看看它的表现如何。fromsklearn.metricsimportclassification_report y_predpipeline.predict(X_test)# Evaluate the modelreportclassification_report(y_test,y_pred)print(report)https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/078f698b5893467b6376a57cd9f327b5.png图片由作者提供总体而言性能是可以接受的。它可以变得更好但让我们继续使用当前模型并将其保存在我们的models文件夹中。importpicklewithopen(..//models//pipeline.pkl,wb)asf:pickle.dump(pipeline,f)当我们的模型准备好后我们将将其部署到生产环境中。我们将将其作为 API 部署并使用 Docker 进行模型容器化。要将模型作为 API 部署让我们在scripts文件夹中创建一个名为app.py的文件。在文件中使用以下代码使模型成为 API。fromfastapiimportFastAPI,HTTPExceptionfrompydanticimportBaseModelimportpickleimportpandasaspd appFastAPI()columns[Pregnancies,Glucose,BloodPressure,SkinThickness,Insulin,BMI,DiabetesPedigreeFunction,Age]dict_res{0:Not-Diabetes,1:Diabetes}pipeline_pathmodels/pipeline.pklwithopen(pipeline_path,rb)aspipeline_file:pipelinepickle.load(pipeline_file)classDataInput(BaseModel):data:listapp.post(/predict)asyncdefpredict(input_data:DataInput):try:dfpd.DataFrame(input_data.data,columnscolumns)predictionspipeline.predict(df)results[dict_res[pred]forpredinpredictions]return{predictions:results}exceptExceptionase:print(Error:,str(e))raiseHTTPException(status_code400,detailstr(e))if__name____main__:importuvicorn uvicorn.run(app,host0.0.0.0,port8000)让我们测试我们是否可以访问模型 API。首先我们将在 CLI 中运行以下代码以启动应用程序。uvicorn scripts.app:app--host0.0.0.0--port8000然后在您的 Jupyter Notebook 中运行以下代码以测试 API。importrequests urlhttp://localhost:8000/predictdata{data:[[1,85,66,29,0,26.6,0.351,31]]}responserequests.post(url,jsondata)print(response.json())确保您传递给 API 的数据位置与您的训练数据位置相同。如果 API 运行良好我们将构建 Docker 镜像并将其推送到仓库。首先让我们在根目录中创建dockerfile。在该文件中填写以下代码。FROM python:3.9-slim WORKDIR/app COPY requirements.txt requirements.txt RUN pip install-r requirements.txt COPY scripts/app.py app.py COPY models models EXPOSE8000CMD[uvicorn,app:app,--host,0.0.0.0,--port,8000]在上面的代码中我们将设置一个 Python 环境并将必要的文件复制到容器中以便在监听端口 8000 时运行 API。使用dockerfile当准备好时我们可以使用以下代码构建镜像。docker build-t username/image_name-f Dockerfile.docker login-u username docker push username/image_name:latest将username更改为您的 Dockerhub 用户名将image_name更改为您首选的应用程序名称。如果您成功您应该在 Dockerhub 上看到您的镜像如我的镜像。因此为什么我们将我们的模型 API 包含在 Docker 中并将其推送到 Dockerhub这确保了在运行应用程序的所有环境中的一致性。在下一节中它还展示了 GitHub Actions 在重新训练模型并将其推回此容器中的强大功能。因此我们只需要拉取镜像来部署模型。运行以下代码从 Dockerhub 拉取镜像并在您的本地环境中运行它。docker login-u username docker pull username/image_name:latest docker run-d-p8000:8000username/image_name:latest通过这种方式我们的模型已在生产环境中。在下一部分我们将了解如何使用 GitHub Actions 和某些触发器重新训练模型。使用 GitHub Actions 进行模型重新训练正如我提到的如果我们要从机器学习模型中获得任何价值那么这是一个持续的项目。这是因为我们不能期望模型每次都能产生相同的质量尤其是在发生漂移的情况下。在本教程中我们将学习如何在生产数据集中检测到数据漂移时执行自动模型重新训练。首先让我们看看我们如何检测数据集中的漂移。让我们用以下代码模拟数据集中的漂移。importnumpyasnpdefintroduce_drift(data,drift_features,drift_amount0.1,random_seed42):np.random.seed(random_seed)drifted_datadata.copy()forfeatureindrift_features:iffeatureindata.columns:drifted_data[feature]np.random.normal(loc0,scaledrift_amount,sizedata.shape[0])returndrifted_data features_to_drift[Glucose,BloodPressure,SkinThickness,Pregnancies]drifted_dataintroduce_drift(X_test,features_to_drift,drift_amount50)drifted_datadrifted_data.reset_index(dropTrue)在上述代码中我们对测试数据中的某些列进行了漂移。您可以通过调整drift_amount来控制数据的漂移程度。我们需要用于教程的训练数据参考和漂移数据新数据。我还会保存目标列我们将在重新训练示例中使用它。reference_data[Outcome]y_train.reset_index(dropTrue)drifted_data[Outcome]y_test.reset_index(dropTrue)drifted_data.to_csv(..//data//new_data.csv,indexFalse)reference_data.to_csv(..//data//reference_data.csv,indexFalse)使用 Evidently我没有任何形式的 Evidently 关联我们将检查生产数据与参考数据相比是否发生了漂移。我们可以用以下代码做到这一点。fromevidently.metric_presetimportDataDriftPresetfromevidently.reportimportReport data_drift_reportReport(metrics[DataDriftPreset(),])data_drift_report.run(current_datadrifted_data.drop(Outcome,axis1),reference_datareference_data.drop(Outcome,axis1),column_mappingNone)report_jsondata_drift_report.as_dict()drift_detectedreport_json[metrics][0][result][dataset_drift]https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/125cb616217cf39ea3bfe483a098b7ba.png作者图片结果显示存在一个我们将用于重新训练自动化的漂移数据集。为了模拟漂移检测让我们在scripts文件夹中创建一个名为drift_detection.py的文件并将以下代码保存到该文件中。importpandasaspdfromevidently.metric_presetimportDataDriftPresetfromevidently.reportimportReport reference_datapd.read_csv(data/reference_data.csv)new_datapd.read_csv(data/new_data.csv)data_drift_reportReport(metrics[DataDriftPreset()])data_drift_report.run(reference_datareference_data.drop(Outcome,axis1),current_datanew_data.drop(Outcome,axis1),column_mappingNone)report_jsondata_drift_report.as_dict()drift_detectedreport_json[metrics][0][result][dataset_drift]ifdrift_detected:print(Data drift detected. Retraining the model.)withopen(drift_detected.txt,w)asf:f.write(drift_detected)else:print(No data drift detected.)withopen(drift_detected.txt,w)asf:f.write(no_drift)在上述代码中我们将布尔结果保存到drift_detected.txt文件中并在检测到漂移时打印信息。如果检测到漂移我们希望重新训练模型。为此我们需要准备训练脚本。在scripts文件夹中创建一个名为train_model.py的文件并填充以下代码。importpandasaspdfromsklearn.pipelineimportPipelinefromsklearn.composeimportColumnTransformerfromsklearn.imputeimportSimpleImputerfromsklearn.preprocessingimportStandardScalerfromsklearn.ensembleimportRandomForestClassifierimportpickle reference_datapd.read_csv(data/reference_data.csv)new_datapd.read_csv(data/new_data.csv)dfpd.concat([reference_data,new_data],ignore_indexTrue)Xdf.drop(Outcome,axis1)ydf[Outcome]numeric_featuresX.columns numeric_transformerPipeline(steps[(imputer,SimpleImputer(strategymean)),(scaler,StandardScaler())])preprocessorColumnTransformer(transformers[(num,numeric_transformer,numeric_features)])pipelinePipeline(steps[(preprocessor,preprocessor),(classifier,RandomForestClassifier(random_state42))])pipeline.fit(X,y)withopen(models/pipeline.pkl,wb)asf:pickle.dump(pipeline,f)上述代码会将训练数据和漂移数据合并到一个新的训练模型中然后使用该模型来训练新模型。这只是一个简化的方法因为现实世界的训练数据需要更多的准备而新模型需要适当的评估。尽管如此有了所有脚本准备就绪我们将准备 GitHub Actions以便在检测到漂移时重新训练模型。我们必须准备包含所有重新训练所需配置的 YAML 文件。因此让我们在.githubworkflows文件夹中创建一个名为mlops_pipeline.yml的文件。确保文件夹名称正确GitHub Actions 需要正确的名称。将以下代码填入mlops_pipeline.yml。name:Diabetes Retraining PipelinewithData Drift Detection on:push:paths:-data/new_data.csvpermissions:contents:write jobs:build:runs-on:ubuntu-latest steps:-name:Checkout code uses:actions/checkoutv2-name:Set up Python uses:actions/setup-pythonv2with:python-version:3.9-name:Install dependencies run:|python-m pip install--upgrade pip pip install-r requirements.txt-name:Run data drift detection run:|python scripts/drift_detection.pycontinue-on-error:true-name:Checkfordata driftid:check_drift run:|ifgrep-qdrift_detecteddrift_detected.txt;then echoData drift detected.echodrifttrue$GITHUB_ENVelseechoNo data drift detected.echodriftfalse$GITHUB_ENV fi shell:bash-name:Model RetrainingifData Drift detectedif:env.drifttruerun:|python scripts/train_model.py-name:Commitandpush updated modelif:env.drifttrueenv:GIT_COMMITTER_NAME:github-actions GIT_COMMITTER_EMAIL:[[email protected]](/cdn-cgi/l/email-protection)run:|git config--globaluser.namegithub-actionsgit config--globaluser.email[[email protected]](/cdn-cgi/l/email-protection)git remoteset-url origin https://x-access-token:${{secrets.ACTIONS_PAT}}github.com/username/image_name.git git add models/pipeline.pkl git commit-mUpdate model after retraining on $(date -u %Y-%m-%d %H:%M:%S UTC)git push-name:Build Docker imageif:env.drifttruerun:|docker build-t username/image_name-f dockerfile.-name:Loginto Docker Hubif:env.drifttruerun:echo${{ secrets.DOCKER_PASSWORD }}|docker login-u${{ secrets.DOCKER_USERNAME }}--password-stdin-name:Push Docker image to Docker Hubif:env.drifttruerun:|docker push username/image_name:latest-name:Notify about the process run:|if[[$GITHUB_ENV*driftfalse*]];then echoNo data drift detected. No retraining necessary.elseechoData drift detected. Model retrained and deployed.fi shell:bash我们在上述 YAML 文件中所做的整体配置结构如下所示。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/118fa3179a95e8899b333522f8f21f8e.png图片由作者提供我们使用的 GitHub Actions 的触发器是在data文件夹内推送new_data.csv文件时。然而只有在检测到漂移时模型重新训练才会运行。如果模型被重新训练我们将它们推回 GitHub 仓库和 Docker Hub。不要忘记将每个username/image_nameDocker 标识符更改为你的标识符。如果你总是使用相同的标识符你也可以创建仓库密钥。一旦所有文件都准备好了你应该将它们推送到你的 GitHub 仓库。然后你可以尝试创建新的漂移数据并将其保存为new_data.csv然后再次尝试将它们推送到仓库。前往你的 GitHub 仓库中的操作标签页。如果它成功运行你应该会看到一个名为build的作业并且状态为成功。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/701c81867e70e31a95310fabea12d3b7.png图片由作者提供点击作业以获取流程的所有详细信息。你可以查看每个步骤的信息以了解流程或查看它是否运行失败。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/5ece7e6da2b9d4987ac22a050ed34508.png图片由作者提供如果你进入仓库中的models你可以检查模型是否已更新。我们使用提交信息来通知模型何时被重新训练。https://github.com/OpenDocCN/towardsdatascience-blog-zh-2024/raw/master/docs/img/46ef719c399f818f467b1ebb9deb4669.png图片由作者提供你也可以检查你的 Docker Hub 仓库看看镜像是否已更新。这就是你需要使用 GitHub Actions 来简化模型重新训练过程的所有内容。你可以根据需要调整所有脚本例如触发器、重新训练条件、数据集等等。如果你需要这篇文章中使用的所有代码我已经将它们推送到这个仓库。结论在这篇文章中我们学习了如何使用 GitHub Actions 来自动化模型重新训练过程。通过使用 YAML 文件设置配置并决定触发器我们可以轻松地使用 GitHub Actions 来简化任何必要的流程。

更多文章