استقرار یک مدل R به عنوان یک API کانتینر شده با داکر ، لوله کشی و RENV

در این پست ، من شما را در کل فرآیند ساخت یک مدل R قدم می زنم ، آن را به عنوان یک API آرام با استفاده از بسته لوله کشی و اضافه کردن ورود دقیق به آن می پردازم. ما هر مرحله را پوشش خواهیم داد ، از جمله عیب یابی موضوعات رایج (مانند کتابخانه های سیستم گمشده) و اطمینان از تکرارپذیری با RENV. در پایان این آموزش ، شما یک تصویر Docker خواهید داشت که از طریق یک نقطه انتهایی API در خدمت مدل شما است – آماده برای آزمایش محلی یا استقرار به خدمات ابری مانند AWS.
می توانید کل کد و dockerfiles را در repo github پیدا کنید.
1. بررسی اجمالی و پیش نیازها
در این راهنما ، ما یک مدل پیش بینی کننده را با استفاده از مجموعه داده های کلاسیک Iris می سازیم و یک طبقه بندی کننده جنگلی تصادفی را آموزش می دهیم. سپس کد R خود را با Docker کانتینر می کنیم و یک API استراحت را از طریق لوله کشی در معرض دید قرار می دهیم. ما با استفاده از بسته Logger ، ورود به سیستم را اضافه می کنیم و وابستگی ها را با RENV برای تکرارپذیری مدیریت می کنیم.
اجزای کلیدی شامل موارد زیر هستند:
- آموزش مدل R: استفاده از RandomForest برای آموزش در مجموعه داده های Iris.
- Containerisation: ایجاد یک محیط R تکرار شونده با استفاده از Docker.
- مدیریت وابستگی: استفاده از RENV برای قفل کردن نسخه های بسته.
- Plumber API: افشای A /پیش بینی نقطه پایانی با مستندات Swagger.
- ورود به سیستم: ورود دقیق درخواست ها و پاسخ های API ، با سیاهههای مربوط به میزبان.
برای نظارت مؤثر در گردش کار استقرار ما ، من یک سیستم ورود به سیستم دوگانه ایجاد کرده ام. در outside_container_logs
دایرکتوری سیاهههای مربوط به توسعه محلی را ضبط می کند – این شامل در حال اجرا و آزمایش اسکریپت های R ، ساخت ظرف Docker و استقرار آینده است. در همین حال ، سیاهههای مربوط به فرآیندهای در حال اجرا در داخل ظرف به inside_container_logs
، که روی /برنامه /سیاهههای مربوط به ظرف نصب شده است. این جدایی ما را قادر می سازد تا بین فعالیتهای قبل از استقرار و رفتار زمان اجرا در داخل ظرف به وضوح تمایز قائل شویم و به ما امکان می دهد تا به طور موثر عیب یابی کنیم و اطمینان حاصل کنیم که هر دو محیط توسعه و تولید ما همانطور که انتظار می رود عمل می کنند.
2. ساختمان و صرفه جویی در یک مدل R
ابتدا یک اسکریپت آموزشی (Train.R) ایجاد می کنیم که یک مدل جنگل تصادفی را با استفاده از مجموعه داده Iris آموزش می دهد. توجه کنید که چگونه دایرکتوری های ورود به سیستم را بر اساس محیط (در داخل یا خارج از ظرف) تنظیم می کنیم ، و نحوه ضبط و جزئیات مدل مدل را تنظیم می کنیم.train.R
# train.R
library(randomForest)
library(logger)
# Choose the log directory based on whether /app/logs exists (inside container) or not (outside)
log_dir <- if (dir.exists("/app/logs")) {
"/app/logs/R_logs"
} else {
"logs/outside_container_logs/R_logs"
}
# Ensure the directory exists; create it if necessary
if (!dir.exists(log_dir)) {
dir.create(log_dir, recursive = TRUE)
}
# Create a timestamp for the log file name
timestamp <- format(Sys.time(), "%Y%m%d_%H%M%S")
log_file <- file.path(log_dir, paste0(timestamp, "_training.log"))
# Configure logger with a unique file name for each run
log_appender(appender_file(log_file))
log_info("Starting model training...")
# Train a random forest model to classify iris species
data(iris)
model <- randomForest(Species ~ ., data = iris, ntree = 100)
log_info("Model training complete.")
# Capture the output of print(model)
model_output <- capture.output(print(model))
# Log the captured output
log_info("Model details:\n{paste(model_output, collapse="\n")}")
# Save the trained model to disk
saveRDS(model, file = "data/model.rds")
log_info("Model saved to model.rds.")
این اسکریپت را اجرا کنید (به عنوان مثال ، به عنوان بخشی از ساخت Docker خود) برای تولید مدل خود و پیشرفت پیشرفت آموزش آن.
3. ساختن تصویر پایه با وابستگی های سیستم و RENV
برای اطمینان از افزایش سنگین وابستگی های سیستم نصب و بسته های R فقط یک بار انجام می شود ، ما یک تصویر پایه ایجاد کردیم. این تصویر شامل R 4.4.1 ، کتابخانه های سیستم لازم (برای بسته هایی مانند TextShaping و RAGG) است و بسته های R را با استفاده از یک فایل Renv.Lock بازیابی می کند.Dockerfile.base
# Use a specific R base image
FROM r-base:4.4.1
# Install system dependencies required by R packages
RUN apt-get update && apt-get install -y \
libcurl4-openssl-dev \
libssl-dev \
libxml2 \
libxml2-dev \
libz-dev \
libsodium-dev \
libfontconfig1-dev \
libfreetype6-dev \
libharfbuzz-dev \
libfribidi-dev \
libpng-dev \
libtiff5-dev \
libjpeg-dev \
build-essential \
pkg-config \
wget \
apt-transport-https \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Create a directory for your application
WORKDIR /app
# Copy your renv lock file to restore R packages
COPY renv.lock /app/renv.lock
# Install renv and restore packages using renv.lock
RUN R -e "install.packages('renv', repos="http://cran.rstudio.com/"); \
renv::restore(repos = c(CRAN = 'http://cran.rstudio.com/'))"
این تصویر (به عنوان مثال برچسب گذاری شده است. r-base-mlops:4.4.1
) به عنوان یک پایه پایدار عمل می کند.
4. تصویر مدل تکراری برای توسعه سریع
با تکیه بر تصویر پایه ، ما یک تصویر تکرار مدل کوچکتر ایجاد کردیم که فقط حاوی کد مدل ما است. این تصویر به ما امکان می دهد بدون نصب مجدد همه وابستگی های سیستم و بسته های R هر بار ، به سرعت در مدل (یا API) خود تکرار کنیم.
ما یک تصویر Docker ایجاد خواهیم کرد که:
- از تصویر پایه R استفاده می کند (در اینجا ، نسخه 4.4.1).
- اسکریپت ها و مدل خود را در ظرف کپی می کند.
- API لوله کشی را در بندر 8080 قرار می دهد.
Dockerfile.model
# Use the prebuilt base image that contains R 4.4.1 and all system dependencies & renv packages
FROM r-base-mlops:4.4.1
# Set the working directory
WORKDIR /app
# Create directories for logs and model data
RUN mkdir -p /app/logs/docker_logs /app/logs/R_logs /app/logs/aws_logs
RUN mkdir -p data
# Copy your model scripts into the container
COPY ./R/train.R /app/train.R
COPY ./R/serve.R /app/serve.R
# Copy the runner script to start the API
COPY ./R/run_api.R /app/run_api.R
# Run the training script to generate or update your model (e.g. data/model.rds)
RUN Rscript train.R
# Expose the port on which your Plumber API will run
EXPOSE 8080
# Start the Plumber API using the runner script
CMD ["Rscript", "run_api.R"]
اسکریپت run_api.r به سادگی سرویس را بارگیری می کند. R را شروع می کند و API را شروع می کند:
# run_api.R
pr <- plumber::plumb("serve.R")
pr$run(host = "0.0.0.0", port = 8080)
این تنظیم تضمین می کند که هنگام تغییر کد مدل خود (در train.R
یا serve.R
) ، شما فقط باید تصویر مدل را بازسازی کنید – نه کل تصویر پایه.
5. مدیریت وابستگی های R با RENV
استفاده از RENV تضمین می کند که ظرف شما نسخه های دقیق بسته های R مورد نیاز پروژه شما را نصب کند. برای تنظیم این برنامه به صورت محلی:
# 1. Initialise renv:
renv::init()
# 2. Install any packages you use (eg. plumber, randomForest, logger, tidyverse).
# 3. Snapshot the environment:
renv::snapshot()
6. ایجاد یک نقطه پایانی API لوله کشی
بعد ، با استفاده از لوله کشی API ایجاد کنید. ما یک نقطه پایانی را تعریف خواهیم کرد /predict
که:
- مدل از پیش آموزش داده شده را بارگیری می کند.
- درخواست JSON ورودی را تجزیه می کند.
- پیش بینی می کند
- جزئیات درخواست و جزئیات پاسخ.
serve.R
# serve.R
library(plumber)
library(randomForest)
library(logger)
library(jsonlite)
# Set up logging
log_dir <- if (dir.exists("/app/logs")) {
"/app/logs/R_logs"
} else {
"logs/outside_container_logs/R_logs"
}
if (!dir.exists(log_dir)) {
dir.create(log_dir, recursive = TRUE)
}
# Create a timestamp for the log file name
timestamp <- format(Sys.time(), "%Y%m%d_%H%M%S")
log_file <- file.path(log_dir, paste0(timestamp, "_request.log"))
log_appender(appender_file(log_file))
# Load the trained model
model <- readRDS("data/model.rds")
#* @filter log_requests
function(req, res) {
log_info("Incoming request: {req$REQUEST_METHOD} {req$PATH_INFO}")
log_info("Request body: {req$postBody}")
plumber::forward()
}
#* @post /predict
#* @serializer json
function(req) {
data <- fromJSON(req$postBody)
input_data <- as.data.frame(data)
pred <- predict(model, input_data)
log_info("Prediction: {paste(pred, collapse=", ")}")
list(prediction = as.character(pred))
}
لوله کشی به طور خودکار مستندات swagger (در دسترس در http: // localhost: 8080/__ اسناد __/) را از این حاشیه نویسی ها تولید می کند.
7. آزمایش API
با استفاده از حلقه
curl -X POST "http://localhost:8080/predict" \
-H "Content-Type: application/json" \
-d '{"Sepal.Length": 5.1, "Sepal.Width": 3.5, "Petal.Length": 1.4, "Petal.Width": 0.2}'
شما باید این نتیجه را بدست آورید:
Prediction: setosa
با استفاده از پستچی
- روش HTTP را برای ارسال تنظیم کنید.
- URL را وارد کنید: http: // localhost: 8080/پیش بینی.
- در زیر برگه هدر ، اضافه کنید
Content-Type
برای کلید با ارزشapplication/json
بشر - در برگه بدنه ، RAW را انتخاب کرده و JSON را از Dropdown انتخاب کنید.
- JSON زیر را بچسبانید:
{
"Sepal.Length": 5.1,
"Sepal.Width": 3.5,
"Petal.Length": 1.4,
"Petal.Width": 0.2
}
- برای مشاهده پاسخ ، روی ارسال کلیک کنید.
با استفاده از بسته HTTR R
library(httr)
library(jsonlite)
url <- "http://localhost:8080/predict"
input <- list(
Sepal.Length = 5.1,
Sepal.Width = 3.5,
Petal.Length = 1.4,
Petal.Width = 0.2
)
response <- POST(
url,
body = toJSON(input, auto_unbox = TRUE),
encode = "raw",
add_headers("Content-Type" = "application/json")
)
result <- content(response, as = "parsed")
print(result)
عیب یابی
1. کانتینر در نصب یک بسته ناکام است
اگر یک بسته نتوانسته است نصب کند (به عنوان مثال ، متن یا ragg) ، پیام خطا را برای کتابخانه های سیستم گمشده بررسی کنید. Dockerfile.base خود را به روز کنید تا بسته های توسعه لازم را نصب کنید (مانند Libharfbuzz-Dev ، Libfribidi-Dev ، Libpng-Dev ، libtiff5-Dev و libjpeg-dev).
2. Docker Volume Mount
هنگام اجرای کانتینر ، از پرچم Docker -v (یا -Volume) استفاده کنید تا فهرست میزبان را به فهرست کانتینر نقشه برداری کنید. به عنوان مثال ، اگر فهرست میزبان شما در فهرست کار فعلی است ، می توانید اجرا کنید:
docker run -v "$(pwd)/logs/inside_container_logs:/app/logs" your_container_image
این دستور به Docker می گوید که فهرست های میزبان خود را/inde_container_logs در ظرف در/برنامه/سیاهههای مربوط سوار کنید.
پس از اجرای کانتینر ، از Docker Container Inspect استفاده کنید تا تأیید کنید که کوه به درستی تنظیم شده است ، و پوشه محلی را بررسی کنید تا اطمینان حاصل شود که سیاهههای مربوط در آنجا نوشته شده اند.