Hoang  Kim

Hoang Kim

1658212962

Cách Tôi Xây Dựng Mô Hình Học Máy Tự động đào Tạo Lại

Hướng dẫn triển khai một hệ thống liên tục thu thập dữ liệu và liên tục đào tạo lại các mô hình Học máy.

Triển khai mô hình Học máy (ML) được đào tạo không phải là phần cuối của quy trình ML. Thay vào đó, nó nên được coi là một cột mốc đầu tiên trong khi các kỹ sư ML phải duy trì hiệu suất của mô hình theo thời gian. Dữ liệu chuỗi thời gian hoặc dữ liệu được sử dụng cho các hệ thống khuyến nghị là những ví dụ điển hình để minh họa quan điểm này; Nếu những người mẫu này không được đào tạo liên tục, họ có thể mất khả năng dự đoán các sự kiện / xu hướng mới.

Trong bài viết này, tôi sẽ giải thích cách thiết lập một hệ thống có thể tự động thu thập đánh giá của khách hàng từ một trang thương mại điện tử , đào tạo lại mô hình NLP khi có dữ liệu mới đến và lưu trữ hiệu quả các phiên bản mô hình theo thời gian.

Tổng quan về kiến ​​trúc

Kiến trúc được tạo thành từ:

  • AWS EC2 —Một môi trường điện toán đám mây, nơi tôi lưu trữ các Vùng chứa luồng khí và MLFlow Docker. Để lưu trữ Luồng không khí, Phiên bản EC2 phải có ít nhất 2 VCPU và RAM 4096 MB.
  • Luồng không khí - Lên lịch và tự động hóa các tác vụ chạy như thu thập dữ liệu từ các trang web, mô hình đào tạo và tải kết quả lên các dịch vụ lưu trữ đám mây.
  • MLFlow - Một nền tảng để quản lý vòng đời ML. Trong dự án, tôi tận dụng nền tảng này để ghi nhật ký các mô hình học máy, số liệu đào tạo và thông số.
  • AWS RDS - Lưu trữ dữ liệu thô và nhật ký đào tạo.
  • AWS S3 —Cửa hàng đã triển khai các phiên bản của mô hình ML.

Sau đây là các bước cấp cao cho kiến ​​trúc này:

  1. Thiết lập vùng chứa Docker.
  2. Thiết kế Nhện thu thập thông tin để thu thập dữ liệu từ trang web.
  3. Xây dựng đường ống ML để xử lý trước, đào tạo và đánh giá kết quả.
  4. Tạo Quy trình làm việc DAGs.

Thư mục dự án

Trước khi bắt đầu, bạn nên có một cái nhìn tổng quan về thư mục dự án. dags, logsvà là datacác pluginsthư mục sẽ được gắn vào vùng chứa Airflow Docker của chúng tôi.

dags
├── __init__.py
├── automated_ml_dag.py
├── clean_archived_dag.py
└── src
logs
data
├── archived
└── staging
plugins
build-images.sh 
docker-compose.yaml 
airflow.Dockerfile
mlflow.Dockerfile
start-main.sh 

Bước 1 - Thiết lập Docker Containers

Trong bước này, chúng tôi xây dựng một môi trường Docker nhiều vùng chứa:

Để bắt đầu, tôi tạo hai Dockerfiles được gọi airflow.Dockerfilemlflow.Dockerfileđể xây dựng các hình ảnh tùy chỉnh cho nhu cầu của dự án.


FROM apache/airflow:2.3.2
USER root
# Set timezone for image
RUN apt-get install -yq tzdata && \
    ln -fs /usr/share/zoneinfo/Asia/Saigon /etc/localtime && \
    dpkg-reconfigure -f noninteractive tzdata
ENV TZ="Asia/Saigon"
USER airflow
# Install Scrapy
RUN pip install scrapy psycopg2-binary mlflow scikit-learn nltk boto3

Sau đó, tôi bắt đầu xây dựng hình ảnh bằng tập lệnh bash:

FROM python:3.7.9
# Set timezone for image
RUN apt-get install -yq tzdata && \
    ln -fs /usr/share/zoneinfo/Asia/Saigon /etc/localtime && \
    dpkg-reconfigure -f noninteractive tzdata
ENV TZ="Asia/Saigon"
# Install 
RUN pip install --upgrade pip setuptools wheel
RUN pip install mlflow psycopg2

Sau đó, tôi bắt đầu xây dựng hình ảnh bằng tập lệnh bash:

#!/bin/bash
source 'env.list'
docker build -f airflow.Dockerfile --rm -t $AIRFLOW_IMAGE_NAME .
docker build -f mlflow.Dockerfile --rm -t $MLFLOW_IMAGE_NAME .

Đối với docker-compose.yaml, chúng ta có thể tham khảo từ một mẫu do Apache Airflow cung cấp tại https://airflow.apache.org/docs/apache-airflow/2.3.2/docker-compose.yaml .

Trong dự án, tôi tùy chỉnh bằng cách thêm một số biến trong airflow-common-env và bao gồm hình ảnh mlflow trong các dịch vụ:

environment:
&airflow-common-env
RDS_USER: ${RDS_USER}
RDS_PASSWORD: ${RDS_PASSWORD}
RDS_HOSTNAME: ${RDS_HOSTNAME}
RDS_NAME: ${RDS_NAME}
MLFLOW_BACKEND_STORE_URI: ${MLFLOW_BACKEND_STORE_URI}
MLFLOW_DEFAULT_ARTIFACT_ROOT: ${MLFLOW_DEFAULT_ARTIFACT_ROOT}
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
 mlflow:
    image: ${MLFLOW_IMAGE_NAME}
    container_name: mlflow
    ports:
      - "5000:5000"
    command: |
      mlflow server --backend-store-uri ${MLFLOW_BACKEND_STORE_URI} --default-artifact-root ${MLFLOW_DEFAULT_ARTIFACT_ROOT} --host "0.0.0.0"
    depends_on:
      <<: *airflow-common-depends-on
      airflow-init:
        condition: service_completed_successfully

Cuối cùng, hãy chạy đoạn mã dưới đây để bắt đầu các vùng chứa:

docker-compose --env-file env.list up airflow-init
docker-compose --env-file env.list up -d

Lưu ý: Đảm bảo một env.listtệp lưu trữ các biến môi trường nằm trong thư mục để chạy thành công lệnh trên.

Trong Docker Desktop, chúng ta sẽ thấy các tín hiệu màu xanh lục của vùng chứa sau khi khởi động thành công.

Bước 2— Thiết kế một con nhện đang thu thập thông tin

Các bài đánh giá thô từ trang web như sau:

Để thu thập đánh giá định kỳ, tôi sử dụng scrapythư viện để thu thập thông tin kết quả thông qua phương pháp API.

Mục đích của việc cạo trong trường hợp này là để thực hành, tôi chọn không hiển thị công khai API trang web. Tuy nhiên, kịch bản dưới đây có thể được tham khảo để xây dựng một con nhện cho mục đích của riêng bạn.

import scrapy
import json
import random
from datetime import datetime

class CrawlSpider(scrapy.Spider):
    name = 'quotes'
    list_match_ids = ['11013350', '11013420', '11013434', '11027829', '11013491']
    match_id = random.choice(list_match_ids)
    offset = 0
    scrape_time = datetime.now()
    # product settings
    product_page_scrape = 2 #must be more than 1
    product_incremented = 20
    product_api = 'https://******/******/v4/search/search_items?by=pop&limit={product_incremented}&match_id={match_id}&newest={offset}&order=desc&page_type=search&scenario=PAGE_CATEGORY&version=2'
    # review settings
    review_page_scrape = 1
    review_incremented = 5
    review_api = 'https://******/******/v2/item/get_ratings?filter=0&flag=1&itemid={itemid}&limit={review_incremented}&offset={offset}&shopid={shopid}&type={type}' 
   
    def __init__(self, db):
        self.db = db
    
    def start_requests(self):
        # Loop product page
        for offset in range(0, self.product_incremented * self.product_page_scrape, self.product_incremented):
            yield scrapy.Request(url=self.product_api.format(
                                                product_incremented=self.product_incremented,
                                                match_id=self.match_id,
                                                offset=offset),
                                callback=self.parse)        
    
    def parse(self, response):
        resp = json.loads(response.body)
        items = resp.get("items")
        rating_list = ['1', '2', '3', '4', '5']
        for rating_type in rating_list:  
            for item in items:
                offset = 0
                item_basic = item.get('item_basic')
                itemid = item_basic.get('itemid')
                shopid = item_basic.get('shopid')
                product = item_basic.get('name')
                while offset < (self.review_incremented * self.review_page_scrape):
                    yield response.follow(self.review_api.format(
                                                    itemid=itemid, 
                                                    shopid=shopid,
                                                    review_incremented=self.review_incremented,
                                                    offset=offset,
                                                    type=rating_type),
                                        callback=self.get_rating,
                                        meta = {
                                                    'product': product,
                                                    'itemid': itemid,
                                                    'shopid': shopid
                                        })
                    offset += self.review_incremented
        
    def get_rating(self, response):
        product = response.meta.get('product')
        itemid = response.meta.get('itemid')
        shopid = response.meta.get('shopid')
        resp = json.loads(response.body)
        ratings = resp.get('data').get('ratings')
        records = []
        for rating in ratings:
            comment = rating.get('comment')
            if len(comment) <= 5: # don't insert if too few characters
                continue
            userid = rating.get('userid')
            ctime = rating.get('ctime')
            orderid = rating.get('orderid')
            rating = rating.get('rating_star')
            record = (userid, product, itemid, shopid, ctime, comment, orderid, rating, self.scrape_time)
            records.append(record)
        self.db.insert_record(records)

Để lưu trữ kết quả đã thu thập vào cơ sở dữ liệu, chúng tôi cũng cần xây dựng lớp Postgres để trình thu thập dữ liệu của chúng tôi có thể gửi kết quả cóp nhặt tới AWS RDS.

import psycopg2
import pandas as pd
import os

class PostgresPipeline:
    def __init__(self):
        self.db_user        = os.environ.get('RDS_USER' ,'') 
        self.db_password    = os.environ.get('RDS_PASSWORD', '')
        self.db_host        = os.environ.get('RDS_HOSTNAME', '')
        self.db_name        = os.environ.get('RDS_NAME', '')
        self.db_schema      = 'dev'
        self.db_table       = 'ecommerce_reviews'
        self.port           = 5432

    def create_conn(self):
        self.client = psycopg2.connect(
                            user=self.db_user,
                            password = self.db_password,
                            host = self.db_host,
                            database = self.db_name,
                            port = self.port)
        self.curr = self.client.cursor()  
        print("Successfully connected to database!")
        
    def close_conn(self):
        self.client.close()
        print("Successfully closed connection to database!")
        
    def create_table(self):
        self.curr.execute(f"""
            CREATE TABLE IF NOT EXISTS {self.db_schema}.{self.db_table} (
                userid text,
                product text,
                itemid text,
                shopid decimal,
                ctime integer,
                comment text,
                orderid text,
                rating integer,
                time_scrape timestamp without time zone,
                PRIMARY KEY (orderid)
            )
        """
        )
        self.client.commit()
             
    def insert_record(self, item):  
        query = """
            INSERT INTO {db_schema}.{db_table} VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
            ON CONFLICT (orderid)
            DO NOTHING
        """.format(db_schema=self.db_schema, db_table=self.db_table)   
        self.curr.executemany(query, item)
        self.client.commit()
    
    def load_data(self, path):
        query = """
            SELECT * FROM {db_schema}.{db_table}
        """.format(db_schema=self.db_schema, db_table=self.db_table)  
        df = pd.read_sql(query, self.client)
        df.to_csv(path, index=False)

Cuối cùng, chúng ta tạo một hàm để tích hợp Scrapy spider với cơ sở dữ liệu.

from scrapy.crawler import CrawlerProcess
from src.spiders.crawl_spider import CrawlSpider
from src.database.postgres_class import PostgresPipeline

def crawl_data():
    db=PostgresPipeline()
    db.create_conn()
    db.create_table()
    process = CrawlerProcess()
    process.crawl(CrawlSpider, db=db)
    process.start()
    db.close_conn()

Mỗi khi quá trình chạy, dữ liệu cóp nhặt được tải lên cơ sở dữ liệu AWS RDS của chúng tôi

Bước 3 - Đường ống ML

Đầu tiên, một đường dẫn cần được xác định về quá trình đào tạo diễn ra như thế nào.

Mục đích của bài viết này không tập trung vào khả năng dự đoán của mô hình; Vì vậy, tôi sử dụng Quy trình NLP cơ bản trong trường hợp này: Mã hóa dữ liệu thô, áp dụng TF-IDF và sử dụng GridSearch để tìm các tham số tốt nhất cho thuật toán (Tôi sử dụng RandomForestClassifier cho dự án này).

import re
import pandas as pd
import pandas as pd

from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from nltk.stem.wordnet import WordNetLemmatizer
from nltk.tokenize import word_tokenize
from nltk.stem.wordnet import WordNetLemmatizer

from sklearn.base import BaseEstimator
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.pipeline import Pipeline
from sklearn.metrics import classification_report
from sklearn.feature_extraction.text import TfidfTransformer, CountVectorizer
from sklearn.ensemble import RandomForestClassifier


def tokenize(text):
    stop_words = stopwords.words("english")
    lemmatizer = WordNetLemmatizer()
    # Remove punctuation characters
    text = re.sub(r"[^a-zA-Z0-9]", " ", text.lower()) 
    # Tokenize text
    tokens = word_tokenize(text)
    # Don't keep texts that are too short
    tokens = [word for word in tokens if len(word) > 2]
    # Lemmatize verbs and remove stop words
    tokens = [lemmatizer.lemmatize(word) for word in tokens if word not in stop_words]  
    return tokens

def group_categories(rating):
    if rating in (1,2):
        category = 0
    elif rating == 3:
        category = 1
    elif rating in (4, 5):
        category = 2
    return category

def preprocessing(df, test_size=0.20, random_state=42):
    # Remove nan rows
    df = df[df['comment'].notnull()]
    # Split train, test
    X = df['comment']
    y = df['rating']
    y = y.apply(lambda rating: group_categories(rating))
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = test_size, random_state = random_state)
    return X_train, X_test, y_train, y_test

class ClfSwitcher(BaseEstimator):

    def __init__(
        self, 
        estimator = RandomForestClassifier(),
    ):
        """
        A Custom BaseEstimator that can switch between classifiers.
        :param estimator: sklearn object - The classifier
        """ 
        self.estimator = estimator

    def fit(self, X, y=None, **kwargs):
        self.estimator.fit(X, y)
        return self

    def predict(self, X, y=None):
        return self.estimator.predict(X)

    def predict_proba(self, X):
        return self.estimator.predict_proba(X)

    def score(self, X, y):
        return self.estimator.score(X, y)
    
def build_model():
    """Return a defined pipeline with gridsearch used to train model"""
    # Add starting verb feature 
    pipeline = Pipeline([
                        ('text_pipeline', Pipeline ([
                                                ('vect', CountVectorizer(tokenizer=tokenize)),
                                                ('tfidf', TfidfTransformer())
                                            ])),
                        ('clf',  ClfSwitcher())
                        ])

    # Set parameters
    parameters = [
            {
                'clf__estimator': [RandomForestClassifier()],
                'text_pipeline__vect__ngram_range': [(1,1), (1,2)],
                'text_pipeline__tfidf__smooth_idf': [True, False],
                'clf__estimator__n_estimators' : [200],
                'clf__estimator__min_samples_split': [2, 5]     
            }
    ]

    # Create gridsearch for pipeline
    model = GridSearchCV(pipeline, param_grid=parameters, verbose=4, cv=5)
    return model

def evaluate_model(model, X_test, y_test):
    """Step to evaluate and print results of fitted model"""
    print("    Model best params: ", model.best_params_)

    # Predict
    y_pred = model.predict(X_test)

    # Get returned metrics report
    report_dict = classification_report(y_test, y_pred, output_dict=True,zero_division=0)
    
    return report_dict

Quá trình hoàn tất, nhưng chúng ta cần thiết kế một quy trình mẹ để ghi lại các chỉ số, tham số và các phiên bản mô hình được đào tạo. Đây là lúc MLFlow đến để giúp chúng tôi. Với MLFlow, chúng tôi có thể dễ dàng theo dõi các phiên bản được đào tạo trong suốt vòng đời ML.

Trong dự án này, bất cứ khi nào một mô hình được đào tạo, các tham số và chỉ số được tải lên AWS RDScác mô hình đã đào tạo được lưu trữ trong AWS S3.

Ngoài ra, để tránh tăng kích thước lưu trữ, chỉ các phiên bản mô hình được đào tạo vượt qua mô hình được triển khai hiện tại; ví dụ về điểm chính xác, được tải lên và thay thế điểm hiện tại.

import nltk
import pandas as pd
import mlflow
import mlflow.sklearn
import os
from src.model.nlp_preprocessing import preprocessing, build_model, evaluate_model
from mlflow.tracking import MlflowClient
from mlflow.entities import ViewType

def train_model(datasource, experiment_name, registered_model, random_state=42):
    # Download nltk packages
    nltk.download('punkt')
    nltk.download('stopwords')
    nltk.download('wordnet')
    nltk.download('omw-1.4')
    nltk.download('averaged_perceptron_tagger')
    nltk.download('vader_lexicon')
    # Get tracking uri and artifact store
    backend_store_uri = os.environ.get("MLFLOW_BACKEND_STORE_URI", "")
    artifact_root = os.environ.get("MLFLOW_DEFAULT_ARTIFACT_ROOT", "")
    # Set tracking uri and model schema
    mlflow.set_tracking_uri(backend_store_uri)
    try:
        mlflow.create_experiment(experiment_name, artifact_root)
    except Exception:
        print("An experiment with same name has been created!")
    mlflow.set_experiment(experiment_name)
    client = MlflowClient()
    experiment = client.get_experiment_by_name(experiment_name)
    list_runs = mlflow.list_run_infos(experiment.experiment_id, run_view_type=ViewType.ACTIVE_ONLY, order_by=['metric.accuracy DESC'])
    
    # Load dataset
    df = pd.read_csv(datasource)
    
    # Train, test split
    X_train, X_test, y_train, y_test = preprocessing(df, random_state=random_state)
    
    # Start process
    with mlflow.start_run():
        model = build_model()
        model.fit(X_train, y_train)
        report_dict= evaluate_model(model, X_test, y_test)
        
        # Get params
        model_params = model.best_params_
        model_name = type(model_params['clf__estimator']).__name__
        smooth_idf = model_params['text_pipeline__tfidf__smooth_idf']
        ngram_range = model_params['text_pipeline__vect__ngram_range']
        n_estimator = model_params['clf__estimator__n_estimators']
        mlflow.log_param("model_name", model_name)
        mlflow.log_param("smooth_idf", smooth_idf)
        mlflow.log_param("ngram_range", ngram_range)
        mlflow.log_param("n_estimator ", n_estimator)
        mlflow.log_param("samples ", df.shape[0])
        
        # Get metrics
        accuracy = report_dict['accuracy']
        avg_precision = report_dict['weighted avg']['precision']
        avg_recall = report_dict['weighted avg']['recall']
        avg_f1 = report_dict['weighted avg']['f1-score']
        avg_support = report_dict['weighted avg']['support']
        negative_f1 = report_dict['0']['f1-score']
        neutral_f1 = report_dict['1']['f1-score']
        positive_f1 = report_dict['2']['f1-score']
        mlflow.log_metric("accuracy", accuracy)
        mlflow.log_metric("avg_precision", avg_precision)
        mlflow.log_metric("avg_recall", avg_recall)
        mlflow.log_metric("avg_f1", avg_f1)
        mlflow.log_metric("avg_support", avg_support)
        mlflow.log_metric("negative_f1", negative_f1)
        mlflow.log_metric("neutral_f1", neutral_f1)
        mlflow.log_metric("positive_f1", positive_f1)
        
        # Print
        print("  Trained successfully!")
        print("  Model %s (n_estimator=%s, ngram_range=%s, smooth_idf=%s):" % (model_name, n_estimator, ngram_range, smooth_idf))
        print("   accuracy: %s" % accuracy)
        print("   avg_precision: %s" % avg_precision)
        print("   avg_recall: %s" % avg_recall)  
        print("   avg_f1: %s" % avg_f1)  
        print("   avg_support: %s" % avg_support)  
        print("   negative_f1: %s" % negative_f1)  
        print("   neutral_f1: %s" % neutral_f1)  
        print("   positive_f1: %s" % positive_f1)  

        # Register the model
        if len(list_runs) == 0:
            print("First time running, use default threshold!")
            threshold = 0.5
        else:
            run_id = list_runs[0].run_id
            threshold = client.get_metric_history(run_id, 'accuracy')[0].value
        # If higher than threshold -> deploy model
        if accuracy > threshold:
            print("New model has higher accuracy than that of the current model, model will be deployed shortly!")
            mlflow.sklearn.log_model(model, "model", registered_model_name=registered_model)
            print("Model has been deployed successfully to S3!")
        else:
            print("Model is NOT deployed because accuracy is not higher than current model or is too low!")
            print("Current model accuracy: ", threshold)

Bước 4 - Tạo Quy trình làm việc DAGs

Khi đến bước này, dự án của chúng ta đã gần hoàn thành. Sau khi thiết kế các phần nhỏ hơn, tất cả những gì chúng ta cần làm bây giờ là tập hợp tất cả lại và biến nó thành một quy trình đầy đủ.

Ví dụ: tôi tạo một DAG luồng không khí (nằm trong ./dagsthư mục) tự động hóa quy trình vào mỗi Thứ Hai, Thứ Tư và Thứ Sáu lúc 8 giờ sáng theo giờ UTC và chỉ vào thứ Sáu, mô hình được đào tạo lại.

from airflow import DAG
from airflow.utils.task_group import TaskGroup
from airflow.operators.python_operator import PythonOperator
from airflow.operators.bash_operator import BashOperator
from airflow.operators.weekday import BranchDayOfWeekOperator
from airflow.operators.empty import EmptyOperator
from src.crawl_data import crawl_data
from src.load_reviews import load_reviews
from src.train_model import train_model
import pendulum

with DAG(
    'automated_ml',
    description='DAGS to crawl data periodically and train model',
    schedule_interval='0 8 * * 1,3,5', # 8 a.m UTC every Mon, Wed, Fri
    start_date=pendulum.datetime(2022, 7, 10, tz="UTC"),
    catchup=False,
    tags=['automated_ml']
) as dag:

    # [START]
    task_crawl = PythonOperator(
                task_id='crawl_data',
                python_callable=crawl_data, 
                dag=dag
            )
    
    branch = BranchDayOfWeekOperator(
            task_id="on_friday_continue",
            follow_task_ids_if_true="load_data",
            follow_task_ids_if_false="empty",
            week_day="Friday",
        )
    
    task_empty = EmptyOperator(
                task_id="empty",
                dag=dag
            )
    
    with TaskGroup(group_id='train_model_group') as train_model_group:
        task_staging = PythonOperator(
                task_id='load_data', 
                python_callable=load_reviews, 
                op_kwargs={'path': 'data/staging/items.csv'},
                dag=dag
            )
    
        task_train = PythonOperator(
                    task_id='train_model',
                    python_callable=train_model,
                    op_kwargs={
                            'datasource':       'data/staging/items.csv',
                            'experiment_name':  'NLP Experiment',
                            'registered_model': 'NLP Model'
                            },
                    dag=dag
                )
    
        bash_command = """
        mv /opt/airflow/data/staging/items.csv /opt/airflow/data/archived/items_$(date '+%Y-%m-%d').csv
        """
        task_archive = BashOperator(
                    task_id="archived_data",
                    bash_command=bash_command,
                    dag=dag
                )
        task_staging >> task_train >> task_archive

    # [END]

    # [FLOW]
    task_crawl  >> branch >> [train_model_group , task_empty]

Trong giao diện người dùng luồng không khí, đoạn mã trên có thể được dịch sang Biểu đồ này. Đối với train_model_group, chúng ta có 3 nhiệm vụ phụ:

  • load_data : tải dữ liệu từ cơ sở dữ liệu AWS RDS và lưu trữ chúng trong data/stagingthư mục.
  • train_model : chạy đường ống đào tạo NLP.
  • archived_data : sau khi đào tạo, chúng tôi di chuyển tất cả các tệp trong data/stagingthư mục sang data/archivedthư mục để theo dõi đầu vào đào tạo.

Nhật ký đào tạo cũng có thể được theo dõi trong Giao diện người dùng luồng không khí:

Để kiểm tra giao diện người dùng MLFlow, hãy điều hướng đến {EC2 Public IP}: 5000 hoặc localhost: 5000 nếu bạn đang chạy trong máy cục bộ.

Trong ví dụ dưới đây, chúng ta có thể thấy rằng chỉ những mô hình có độ chính xác cao hơn mô hình hiện tại mới được triển khai ( cột Mô hình ).

Cũng dễ dàng theo dõi ID của mô hình đã triển khai từ MLFlow UI và tải xuống mô hình được đào tạo từ AWS S3.

 

🙌🙌 Đây là phần cuối của bài viết và tôi hy vọng bạn thích nó.

Nếu bạn thấy bài viết này có nhiều thông tin, hãy like và theo dõi tôi để được thông báo về những bài viết sắp tới của tôi ✌️

Liên kết: https://medium.com/faun/how-i-built-an-automated-retraining-machine-learning-model-6a6032f37bb9

#machinelearning 

What is GEEK

Buddha Community

Cách Tôi Xây Dựng Mô Hình Học Máy Tự động đào Tạo Lại
Hoang  Kim

Hoang Kim

1658212962

Cách Tôi Xây Dựng Mô Hình Học Máy Tự động đào Tạo Lại

Hướng dẫn triển khai một hệ thống liên tục thu thập dữ liệu và liên tục đào tạo lại các mô hình Học máy.

Triển khai mô hình Học máy (ML) được đào tạo không phải là phần cuối của quy trình ML. Thay vào đó, nó nên được coi là một cột mốc đầu tiên trong khi các kỹ sư ML phải duy trì hiệu suất của mô hình theo thời gian. Dữ liệu chuỗi thời gian hoặc dữ liệu được sử dụng cho các hệ thống khuyến nghị là những ví dụ điển hình để minh họa quan điểm này; Nếu những người mẫu này không được đào tạo liên tục, họ có thể mất khả năng dự đoán các sự kiện / xu hướng mới.

Trong bài viết này, tôi sẽ giải thích cách thiết lập một hệ thống có thể tự động thu thập đánh giá của khách hàng từ một trang thương mại điện tử , đào tạo lại mô hình NLP khi có dữ liệu mới đến và lưu trữ hiệu quả các phiên bản mô hình theo thời gian.

Tổng quan về kiến ​​trúc

Kiến trúc được tạo thành từ:

  • AWS EC2 —Một môi trường điện toán đám mây, nơi tôi lưu trữ các Vùng chứa luồng khí và MLFlow Docker. Để lưu trữ Luồng không khí, Phiên bản EC2 phải có ít nhất 2 VCPU và RAM 4096 MB.
  • Luồng không khí - Lên lịch và tự động hóa các tác vụ chạy như thu thập dữ liệu từ các trang web, mô hình đào tạo và tải kết quả lên các dịch vụ lưu trữ đám mây.
  • MLFlow - Một nền tảng để quản lý vòng đời ML. Trong dự án, tôi tận dụng nền tảng này để ghi nhật ký các mô hình học máy, số liệu đào tạo và thông số.
  • AWS RDS - Lưu trữ dữ liệu thô và nhật ký đào tạo.
  • AWS S3 —Cửa hàng đã triển khai các phiên bản của mô hình ML.

Sau đây là các bước cấp cao cho kiến ​​trúc này:

  1. Thiết lập vùng chứa Docker.
  2. Thiết kế Nhện thu thập thông tin để thu thập dữ liệu từ trang web.
  3. Xây dựng đường ống ML để xử lý trước, đào tạo và đánh giá kết quả.
  4. Tạo Quy trình làm việc DAGs.

Thư mục dự án

Trước khi bắt đầu, bạn nên có một cái nhìn tổng quan về thư mục dự án. dags, logsvà là datacác pluginsthư mục sẽ được gắn vào vùng chứa Airflow Docker của chúng tôi.

dags
├── __init__.py
├── automated_ml_dag.py
├── clean_archived_dag.py
└── src
logs
data
├── archived
└── staging
plugins
build-images.sh 
docker-compose.yaml 
airflow.Dockerfile
mlflow.Dockerfile
start-main.sh 

Bước 1 - Thiết lập Docker Containers

Trong bước này, chúng tôi xây dựng một môi trường Docker nhiều vùng chứa:

Để bắt đầu, tôi tạo hai Dockerfiles được gọi airflow.Dockerfilemlflow.Dockerfileđể xây dựng các hình ảnh tùy chỉnh cho nhu cầu của dự án.


FROM apache/airflow:2.3.2
USER root
# Set timezone for image
RUN apt-get install -yq tzdata && \
    ln -fs /usr/share/zoneinfo/Asia/Saigon /etc/localtime && \
    dpkg-reconfigure -f noninteractive tzdata
ENV TZ="Asia/Saigon"
USER airflow
# Install Scrapy
RUN pip install scrapy psycopg2-binary mlflow scikit-learn nltk boto3

Sau đó, tôi bắt đầu xây dựng hình ảnh bằng tập lệnh bash:

FROM python:3.7.9
# Set timezone for image
RUN apt-get install -yq tzdata && \
    ln -fs /usr/share/zoneinfo/Asia/Saigon /etc/localtime && \
    dpkg-reconfigure -f noninteractive tzdata
ENV TZ="Asia/Saigon"
# Install 
RUN pip install --upgrade pip setuptools wheel
RUN pip install mlflow psycopg2

Sau đó, tôi bắt đầu xây dựng hình ảnh bằng tập lệnh bash:

#!/bin/bash
source 'env.list'
docker build -f airflow.Dockerfile --rm -t $AIRFLOW_IMAGE_NAME .
docker build -f mlflow.Dockerfile --rm -t $MLFLOW_IMAGE_NAME .

Đối với docker-compose.yaml, chúng ta có thể tham khảo từ một mẫu do Apache Airflow cung cấp tại https://airflow.apache.org/docs/apache-airflow/2.3.2/docker-compose.yaml .

Trong dự án, tôi tùy chỉnh bằng cách thêm một số biến trong airflow-common-env và bao gồm hình ảnh mlflow trong các dịch vụ:

environment:
&airflow-common-env
RDS_USER: ${RDS_USER}
RDS_PASSWORD: ${RDS_PASSWORD}
RDS_HOSTNAME: ${RDS_HOSTNAME}
RDS_NAME: ${RDS_NAME}
MLFLOW_BACKEND_STORE_URI: ${MLFLOW_BACKEND_STORE_URI}
MLFLOW_DEFAULT_ARTIFACT_ROOT: ${MLFLOW_DEFAULT_ARTIFACT_ROOT}
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
 mlflow:
    image: ${MLFLOW_IMAGE_NAME}
    container_name: mlflow
    ports:
      - "5000:5000"
    command: |
      mlflow server --backend-store-uri ${MLFLOW_BACKEND_STORE_URI} --default-artifact-root ${MLFLOW_DEFAULT_ARTIFACT_ROOT} --host "0.0.0.0"
    depends_on:
      <<: *airflow-common-depends-on
      airflow-init:
        condition: service_completed_successfully

Cuối cùng, hãy chạy đoạn mã dưới đây để bắt đầu các vùng chứa:

docker-compose --env-file env.list up airflow-init
docker-compose --env-file env.list up -d

Lưu ý: Đảm bảo một env.listtệp lưu trữ các biến môi trường nằm trong thư mục để chạy thành công lệnh trên.

Trong Docker Desktop, chúng ta sẽ thấy các tín hiệu màu xanh lục của vùng chứa sau khi khởi động thành công.

Bước 2— Thiết kế một con nhện đang thu thập thông tin

Các bài đánh giá thô từ trang web như sau:

Để thu thập đánh giá định kỳ, tôi sử dụng scrapythư viện để thu thập thông tin kết quả thông qua phương pháp API.

Mục đích của việc cạo trong trường hợp này là để thực hành, tôi chọn không hiển thị công khai API trang web. Tuy nhiên, kịch bản dưới đây có thể được tham khảo để xây dựng một con nhện cho mục đích của riêng bạn.

import scrapy
import json
import random
from datetime import datetime

class CrawlSpider(scrapy.Spider):
    name = 'quotes'
    list_match_ids = ['11013350', '11013420', '11013434', '11027829', '11013491']
    match_id = random.choice(list_match_ids)
    offset = 0
    scrape_time = datetime.now()
    # product settings
    product_page_scrape = 2 #must be more than 1
    product_incremented = 20
    product_api = 'https://******/******/v4/search/search_items?by=pop&limit={product_incremented}&match_id={match_id}&newest={offset}&order=desc&page_type=search&scenario=PAGE_CATEGORY&version=2'
    # review settings
    review_page_scrape = 1
    review_incremented = 5
    review_api = 'https://******/******/v2/item/get_ratings?filter=0&flag=1&itemid={itemid}&limit={review_incremented}&offset={offset}&shopid={shopid}&type={type}' 
   
    def __init__(self, db):
        self.db = db
    
    def start_requests(self):
        # Loop product page
        for offset in range(0, self.product_incremented * self.product_page_scrape, self.product_incremented):
            yield scrapy.Request(url=self.product_api.format(
                                                product_incremented=self.product_incremented,
                                                match_id=self.match_id,
                                                offset=offset),
                                callback=self.parse)        
    
    def parse(self, response):
        resp = json.loads(response.body)
        items = resp.get("items")
        rating_list = ['1', '2', '3', '4', '5']
        for rating_type in rating_list:  
            for item in items:
                offset = 0
                item_basic = item.get('item_basic')
                itemid = item_basic.get('itemid')
                shopid = item_basic.get('shopid')
                product = item_basic.get('name')
                while offset < (self.review_incremented * self.review_page_scrape):
                    yield response.follow(self.review_api.format(
                                                    itemid=itemid, 
                                                    shopid=shopid,
                                                    review_incremented=self.review_incremented,
                                                    offset=offset,
                                                    type=rating_type),
                                        callback=self.get_rating,
                                        meta = {
                                                    'product': product,
                                                    'itemid': itemid,
                                                    'shopid': shopid
                                        })
                    offset += self.review_incremented
        
    def get_rating(self, response):
        product = response.meta.get('product')
        itemid = response.meta.get('itemid')
        shopid = response.meta.get('shopid')
        resp = json.loads(response.body)
        ratings = resp.get('data').get('ratings')
        records = []
        for rating in ratings:
            comment = rating.get('comment')
            if len(comment) <= 5: # don't insert if too few characters
                continue
            userid = rating.get('userid')
            ctime = rating.get('ctime')
            orderid = rating.get('orderid')
            rating = rating.get('rating_star')
            record = (userid, product, itemid, shopid, ctime, comment, orderid, rating, self.scrape_time)
            records.append(record)
        self.db.insert_record(records)

Để lưu trữ kết quả đã thu thập vào cơ sở dữ liệu, chúng tôi cũng cần xây dựng lớp Postgres để trình thu thập dữ liệu của chúng tôi có thể gửi kết quả cóp nhặt tới AWS RDS.

import psycopg2
import pandas as pd
import os

class PostgresPipeline:
    def __init__(self):
        self.db_user        = os.environ.get('RDS_USER' ,'') 
        self.db_password    = os.environ.get('RDS_PASSWORD', '')
        self.db_host        = os.environ.get('RDS_HOSTNAME', '')
        self.db_name        = os.environ.get('RDS_NAME', '')
        self.db_schema      = 'dev'
        self.db_table       = 'ecommerce_reviews'
        self.port           = 5432

    def create_conn(self):
        self.client = psycopg2.connect(
                            user=self.db_user,
                            password = self.db_password,
                            host = self.db_host,
                            database = self.db_name,
                            port = self.port)
        self.curr = self.client.cursor()  
        print("Successfully connected to database!")
        
    def close_conn(self):
        self.client.close()
        print("Successfully closed connection to database!")
        
    def create_table(self):
        self.curr.execute(f"""
            CREATE TABLE IF NOT EXISTS {self.db_schema}.{self.db_table} (
                userid text,
                product text,
                itemid text,
                shopid decimal,
                ctime integer,
                comment text,
                orderid text,
                rating integer,
                time_scrape timestamp without time zone,
                PRIMARY KEY (orderid)
            )
        """
        )
        self.client.commit()
             
    def insert_record(self, item):  
        query = """
            INSERT INTO {db_schema}.{db_table} VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
            ON CONFLICT (orderid)
            DO NOTHING
        """.format(db_schema=self.db_schema, db_table=self.db_table)   
        self.curr.executemany(query, item)
        self.client.commit()
    
    def load_data(self, path):
        query = """
            SELECT * FROM {db_schema}.{db_table}
        """.format(db_schema=self.db_schema, db_table=self.db_table)  
        df = pd.read_sql(query, self.client)
        df.to_csv(path, index=False)

Cuối cùng, chúng ta tạo một hàm để tích hợp Scrapy spider với cơ sở dữ liệu.

from scrapy.crawler import CrawlerProcess
from src.spiders.crawl_spider import CrawlSpider
from src.database.postgres_class import PostgresPipeline

def crawl_data():
    db=PostgresPipeline()
    db.create_conn()
    db.create_table()
    process = CrawlerProcess()
    process.crawl(CrawlSpider, db=db)
    process.start()
    db.close_conn()

Mỗi khi quá trình chạy, dữ liệu cóp nhặt được tải lên cơ sở dữ liệu AWS RDS của chúng tôi

Bước 3 - Đường ống ML

Đầu tiên, một đường dẫn cần được xác định về quá trình đào tạo diễn ra như thế nào.

Mục đích của bài viết này không tập trung vào khả năng dự đoán của mô hình; Vì vậy, tôi sử dụng Quy trình NLP cơ bản trong trường hợp này: Mã hóa dữ liệu thô, áp dụng TF-IDF và sử dụng GridSearch để tìm các tham số tốt nhất cho thuật toán (Tôi sử dụng RandomForestClassifier cho dự án này).

import re
import pandas as pd
import pandas as pd

from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
from nltk.stem.wordnet import WordNetLemmatizer
from nltk.tokenize import word_tokenize
from nltk.stem.wordnet import WordNetLemmatizer

from sklearn.base import BaseEstimator
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.pipeline import Pipeline
from sklearn.metrics import classification_report
from sklearn.feature_extraction.text import TfidfTransformer, CountVectorizer
from sklearn.ensemble import RandomForestClassifier


def tokenize(text):
    stop_words = stopwords.words("english")
    lemmatizer = WordNetLemmatizer()
    # Remove punctuation characters
    text = re.sub(r"[^a-zA-Z0-9]", " ", text.lower()) 
    # Tokenize text
    tokens = word_tokenize(text)
    # Don't keep texts that are too short
    tokens = [word for word in tokens if len(word) > 2]
    # Lemmatize verbs and remove stop words
    tokens = [lemmatizer.lemmatize(word) for word in tokens if word not in stop_words]  
    return tokens

def group_categories(rating):
    if rating in (1,2):
        category = 0
    elif rating == 3:
        category = 1
    elif rating in (4, 5):
        category = 2
    return category

def preprocessing(df, test_size=0.20, random_state=42):
    # Remove nan rows
    df = df[df['comment'].notnull()]
    # Split train, test
    X = df['comment']
    y = df['rating']
    y = y.apply(lambda rating: group_categories(rating))
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = test_size, random_state = random_state)
    return X_train, X_test, y_train, y_test

class ClfSwitcher(BaseEstimator):

    def __init__(
        self, 
        estimator = RandomForestClassifier(),
    ):
        """
        A Custom BaseEstimator that can switch between classifiers.
        :param estimator: sklearn object - The classifier
        """ 
        self.estimator = estimator

    def fit(self, X, y=None, **kwargs):
        self.estimator.fit(X, y)
        return self

    def predict(self, X, y=None):
        return self.estimator.predict(X)

    def predict_proba(self, X):
        return self.estimator.predict_proba(X)

    def score(self, X, y):
        return self.estimator.score(X, y)
    
def build_model():
    """Return a defined pipeline with gridsearch used to train model"""
    # Add starting verb feature 
    pipeline = Pipeline([
                        ('text_pipeline', Pipeline ([
                                                ('vect', CountVectorizer(tokenizer=tokenize)),
                                                ('tfidf', TfidfTransformer())
                                            ])),
                        ('clf',  ClfSwitcher())
                        ])

    # Set parameters
    parameters = [
            {
                'clf__estimator': [RandomForestClassifier()],
                'text_pipeline__vect__ngram_range': [(1,1), (1,2)],
                'text_pipeline__tfidf__smooth_idf': [True, False],
                'clf__estimator__n_estimators' : [200],
                'clf__estimator__min_samples_split': [2, 5]     
            }
    ]

    # Create gridsearch for pipeline
    model = GridSearchCV(pipeline, param_grid=parameters, verbose=4, cv=5)
    return model

def evaluate_model(model, X_test, y_test):
    """Step to evaluate and print results of fitted model"""
    print("    Model best params: ", model.best_params_)

    # Predict
    y_pred = model.predict(X_test)

    # Get returned metrics report
    report_dict = classification_report(y_test, y_pred, output_dict=True,zero_division=0)
    
    return report_dict

Quá trình hoàn tất, nhưng chúng ta cần thiết kế một quy trình mẹ để ghi lại các chỉ số, tham số và các phiên bản mô hình được đào tạo. Đây là lúc MLFlow đến để giúp chúng tôi. Với MLFlow, chúng tôi có thể dễ dàng theo dõi các phiên bản được đào tạo trong suốt vòng đời ML.

Trong dự án này, bất cứ khi nào một mô hình được đào tạo, các tham số và chỉ số được tải lên AWS RDScác mô hình đã đào tạo được lưu trữ trong AWS S3.

Ngoài ra, để tránh tăng kích thước lưu trữ, chỉ các phiên bản mô hình được đào tạo vượt qua mô hình được triển khai hiện tại; ví dụ về điểm chính xác, được tải lên và thay thế điểm hiện tại.

import nltk
import pandas as pd
import mlflow
import mlflow.sklearn
import os
from src.model.nlp_preprocessing import preprocessing, build_model, evaluate_model
from mlflow.tracking import MlflowClient
from mlflow.entities import ViewType

def train_model(datasource, experiment_name, registered_model, random_state=42):
    # Download nltk packages
    nltk.download('punkt')
    nltk.download('stopwords')
    nltk.download('wordnet')
    nltk.download('omw-1.4')
    nltk.download('averaged_perceptron_tagger')
    nltk.download('vader_lexicon')
    # Get tracking uri and artifact store
    backend_store_uri = os.environ.get("MLFLOW_BACKEND_STORE_URI", "")
    artifact_root = os.environ.get("MLFLOW_DEFAULT_ARTIFACT_ROOT", "")
    # Set tracking uri and model schema
    mlflow.set_tracking_uri(backend_store_uri)
    try:
        mlflow.create_experiment(experiment_name, artifact_root)
    except Exception:
        print("An experiment with same name has been created!")
    mlflow.set_experiment(experiment_name)
    client = MlflowClient()
    experiment = client.get_experiment_by_name(experiment_name)
    list_runs = mlflow.list_run_infos(experiment.experiment_id, run_view_type=ViewType.ACTIVE_ONLY, order_by=['metric.accuracy DESC'])
    
    # Load dataset
    df = pd.read_csv(datasource)
    
    # Train, test split
    X_train, X_test, y_train, y_test = preprocessing(df, random_state=random_state)
    
    # Start process
    with mlflow.start_run():
        model = build_model()
        model.fit(X_train, y_train)
        report_dict= evaluate_model(model, X_test, y_test)
        
        # Get params
        model_params = model.best_params_
        model_name = type(model_params['clf__estimator']).__name__
        smooth_idf = model_params['text_pipeline__tfidf__smooth_idf']
        ngram_range = model_params['text_pipeline__vect__ngram_range']
        n_estimator = model_params['clf__estimator__n_estimators']
        mlflow.log_param("model_name", model_name)
        mlflow.log_param("smooth_idf", smooth_idf)
        mlflow.log_param("ngram_range", ngram_range)
        mlflow.log_param("n_estimator ", n_estimator)
        mlflow.log_param("samples ", df.shape[0])
        
        # Get metrics
        accuracy = report_dict['accuracy']
        avg_precision = report_dict['weighted avg']['precision']
        avg_recall = report_dict['weighted avg']['recall']
        avg_f1 = report_dict['weighted avg']['f1-score']
        avg_support = report_dict['weighted avg']['support']
        negative_f1 = report_dict['0']['f1-score']
        neutral_f1 = report_dict['1']['f1-score']
        positive_f1 = report_dict['2']['f1-score']
        mlflow.log_metric("accuracy", accuracy)
        mlflow.log_metric("avg_precision", avg_precision)
        mlflow.log_metric("avg_recall", avg_recall)
        mlflow.log_metric("avg_f1", avg_f1)
        mlflow.log_metric("avg_support", avg_support)
        mlflow.log_metric("negative_f1", negative_f1)
        mlflow.log_metric("neutral_f1", neutral_f1)
        mlflow.log_metric("positive_f1", positive_f1)
        
        # Print
        print("  Trained successfully!")
        print("  Model %s (n_estimator=%s, ngram_range=%s, smooth_idf=%s):" % (model_name, n_estimator, ngram_range, smooth_idf))
        print("   accuracy: %s" % accuracy)
        print("   avg_precision: %s" % avg_precision)
        print("   avg_recall: %s" % avg_recall)  
        print("   avg_f1: %s" % avg_f1)  
        print("   avg_support: %s" % avg_support)  
        print("   negative_f1: %s" % negative_f1)  
        print("   neutral_f1: %s" % neutral_f1)  
        print("   positive_f1: %s" % positive_f1)  

        # Register the model
        if len(list_runs) == 0:
            print("First time running, use default threshold!")
            threshold = 0.5
        else:
            run_id = list_runs[0].run_id
            threshold = client.get_metric_history(run_id, 'accuracy')[0].value
        # If higher than threshold -> deploy model
        if accuracy > threshold:
            print("New model has higher accuracy than that of the current model, model will be deployed shortly!")
            mlflow.sklearn.log_model(model, "model", registered_model_name=registered_model)
            print("Model has been deployed successfully to S3!")
        else:
            print("Model is NOT deployed because accuracy is not higher than current model or is too low!")
            print("Current model accuracy: ", threshold)

Bước 4 - Tạo Quy trình làm việc DAGs

Khi đến bước này, dự án của chúng ta đã gần hoàn thành. Sau khi thiết kế các phần nhỏ hơn, tất cả những gì chúng ta cần làm bây giờ là tập hợp tất cả lại và biến nó thành một quy trình đầy đủ.

Ví dụ: tôi tạo một DAG luồng không khí (nằm trong ./dagsthư mục) tự động hóa quy trình vào mỗi Thứ Hai, Thứ Tư và Thứ Sáu lúc 8 giờ sáng theo giờ UTC và chỉ vào thứ Sáu, mô hình được đào tạo lại.

from airflow import DAG
from airflow.utils.task_group import TaskGroup
from airflow.operators.python_operator import PythonOperator
from airflow.operators.bash_operator import BashOperator
from airflow.operators.weekday import BranchDayOfWeekOperator
from airflow.operators.empty import EmptyOperator
from src.crawl_data import crawl_data
from src.load_reviews import load_reviews
from src.train_model import train_model
import pendulum

with DAG(
    'automated_ml',
    description='DAGS to crawl data periodically and train model',
    schedule_interval='0 8 * * 1,3,5', # 8 a.m UTC every Mon, Wed, Fri
    start_date=pendulum.datetime(2022, 7, 10, tz="UTC"),
    catchup=False,
    tags=['automated_ml']
) as dag:

    # [START]
    task_crawl = PythonOperator(
                task_id='crawl_data',
                python_callable=crawl_data, 
                dag=dag
            )
    
    branch = BranchDayOfWeekOperator(
            task_id="on_friday_continue",
            follow_task_ids_if_true="load_data",
            follow_task_ids_if_false="empty",
            week_day="Friday",
        )
    
    task_empty = EmptyOperator(
                task_id="empty",
                dag=dag
            )
    
    with TaskGroup(group_id='train_model_group') as train_model_group:
        task_staging = PythonOperator(
                task_id='load_data', 
                python_callable=load_reviews, 
                op_kwargs={'path': 'data/staging/items.csv'},
                dag=dag
            )
    
        task_train = PythonOperator(
                    task_id='train_model',
                    python_callable=train_model,
                    op_kwargs={
                            'datasource':       'data/staging/items.csv',
                            'experiment_name':  'NLP Experiment',
                            'registered_model': 'NLP Model'
                            },
                    dag=dag
                )
    
        bash_command = """
        mv /opt/airflow/data/staging/items.csv /opt/airflow/data/archived/items_$(date '+%Y-%m-%d').csv
        """
        task_archive = BashOperator(
                    task_id="archived_data",
                    bash_command=bash_command,
                    dag=dag
                )
        task_staging >> task_train >> task_archive

    # [END]

    # [FLOW]
    task_crawl  >> branch >> [train_model_group , task_empty]

Trong giao diện người dùng luồng không khí, đoạn mã trên có thể được dịch sang Biểu đồ này. Đối với train_model_group, chúng ta có 3 nhiệm vụ phụ:

  • load_data : tải dữ liệu từ cơ sở dữ liệu AWS RDS và lưu trữ chúng trong data/stagingthư mục.
  • train_model : chạy đường ống đào tạo NLP.
  • archived_data : sau khi đào tạo, chúng tôi di chuyển tất cả các tệp trong data/stagingthư mục sang data/archivedthư mục để theo dõi đầu vào đào tạo.

Nhật ký đào tạo cũng có thể được theo dõi trong Giao diện người dùng luồng không khí:

Để kiểm tra giao diện người dùng MLFlow, hãy điều hướng đến {EC2 Public IP}: 5000 hoặc localhost: 5000 nếu bạn đang chạy trong máy cục bộ.

Trong ví dụ dưới đây, chúng ta có thể thấy rằng chỉ những mô hình có độ chính xác cao hơn mô hình hiện tại mới được triển khai ( cột Mô hình ).

Cũng dễ dàng theo dõi ID của mô hình đã triển khai từ MLFlow UI và tải xuống mô hình được đào tạo từ AWS S3.

 

🙌🙌 Đây là phần cuối của bài viết và tôi hy vọng bạn thích nó.

Nếu bạn thấy bài viết này có nhiều thông tin, hãy like và theo dõi tôi để được thông báo về những bài viết sắp tới của tôi ✌️

Liên kết: https://medium.com/faun/how-i-built-an-automated-retraining-machine-learning-model-6a6032f37bb9

#machinelearning 

35.000+ Tủ locker, tủ nhân viên cao cấp chính hãng l Nam Thuy Corp

35.000+ Tủ locker, tủ nhân viên cao cấp chính hãng l Nam Thuy Corp




 

Việc trang bị tủ locker cho các trường học là điều vô cùng cần thiết để giúp học sinh có ý thức và trách nhiệm hơn trong việc bảo quản tài sản cá nhân.


 

Website: https://namthuycorp.com/danh-muc-san-pham/tu-locker/


 

#tủ_sắt_locker #locker #tu_sat_locker #tu_locker #tủ_locker_sắt #tủ_nhân_viên #tu_locker_sat #tủ_locker_giá_rẻ #tu_locker_gia_re #tủ_cá_nhân_locker #tủ_sắt_nhiều_ngăn #tủ_đựng_đồ_nhân_viên


 

CÔNG TY TNHH QUỐC TẾ NAM THỦY

Công ty thành viên trực thuộc Nam Thủy Group


 

Địa chỉ: SH02-22, Sari Town, KĐT Sala, 10 Mai Chí Thọ,

Phường An Lợi Đông, Quận 2, TP. Hồ Chí Minh


 

Điện thoại: (028) 62700527          Hotline: 0909 420 804


 

Email: info@namthuycorp.com



 

Tủ iLocker

Tủ iLocker mang nhiều đặc điểm nổi bật, cùng các phương thức bảo mật khác nhau như Fingerprint, RFID, Face ID hoặc QR code được xem là giải pháp lưu trữ tối ưu của Smart Locker

#tủ_locker #tủ_sắt_locker   #locker #tu_sat_locker #tu_locker #tủ_locker_sắt #tủ_nhân_viên #tu_locker_sat #tủ_locker_giá rẻ #tu_locker_gia_re #tủ_cá_nhân_locker #tủ_sắt_nhiều_ngăn #tủ_đựng_đồ_nhân_viên

Website: 

tủ locker

tủ sắt locker

locker

tu sat locker

tu locker


 

Tủ locker

Tủ locker dòng W900 là sản phẩm có thể xếp chồng lên được 3 tầng cho 1 cột, có thể kết hợp nhiều cột tủ lại với nhau theo mong muốn (không giới hạn số lượng cột tủ)

#tủ_locker #tủ_sắt_locker #locker #tu_sat_locker #tu_locker #tủ_locker_sắt #tủ_nhân_viên #tu_locker_sat #tủ_locker_giá rẻ #tu_locker_gia_re #tủ_cá_nhân_locker #tủ_sắt_nhiều_ngăn #tủ_đựng_đồ_nhân_viên

Website: 

tủ locker

tủ locker giá rẻ

tu locker gia re

tủ cá nhân locker

tủ sắt nhiều ngăn


 

tủ locker

Hệ thống tủ locker trong trường học giúp cho các học sinh sinh viên có một môi trường học tập hiện đại, thoải mái và an toàn hơn.

#tủ_locker #tủ_sắt_locker #locker #tu_sat_locker #tu_locker #tủ_locker_sắt #tủ_nhân_viên #tu_locker_sat #tủ_locker_giá rẻ #tu_locker_gia_re #tủ_cá_nhân_locker #tủ_sắt_nhiều_ngăn #tủ_đựng_đồ_nhân_viên

Website: 

locker

tủ đựng đồ nhân viên

tủ locker

tủ sắt locker

locker