+1

Triển Khai Mô Hình AI Trên Máy Chủ VPS Ubuntu 20.04 LTS

1. Giới thiệu:

Bạn đã bao giờ tự hỏi làm thế nào để biến một mô hình AI thành một ứng dụng thực tế trên web? Series mới này sẽ đưa bạn qua từng bước từ A đến Z:

✅ Chuẩn bị môi trường làm việc

✅ Đóng gói và tối ưu hóa mô hình AI

✅ Triển khai trên các nền tảng web phổ biến

Dành cho ai?

Những ai đang muốn áp dụng AI vào các dự án thực tế.Các bạn đam mê công nghệ và muốn học cách tạo ra các ứng dụng thông minh.

Bạn sẽ nhận được gì?

💡 Kinh nghiệm triển khai mô hình AI thực tế.

💡 Các kỹ thuật và công cụ hữu ích để tối ưu hóa quy trình.

💡 Cách làm rõ ràng, dễ hiểu, giúp bạn tiếp cận nhanh chóng.

Tiếp nối series trước, chúng ta đã triển khai thành công một trang web chuyên nghiệp với tên miền và HTTPS, giúp mọi người có thể dễ dàng truy cập từ bất kỳ đâu. Bài viết này sẽ tiếp tục chuỗi hướng dẫn, nhưng với một chủ đề nâng cao hơn: Triển khai một mô hình AI lên website.

Trong bài viết này, chúng ta sẽ triển khai một mô hình AI lên website bằng cách sử dụng các công cụ:

  • Uvicorn: Server nhẹ để chạy ứng dụng Python.
  • FastAPI: Framework mạnh mẽ, hiện đại để xây dựng API.
  • Ultralytics: Thư viện hỗ trợ mô hình cho phát hiện vật thể.

Bài viết hướng dẫn từng bước từ chuẩn bị môi trường, xây dựng API đến cấu hình Nginx để ứng dụng AI của bạn sẵn sàng hoạt động trên VPS.

Hãy bắt đầu ngay!

2. Chuẩn bị môi trường

2.1 Cài đặt các công cụ cần thiết

Trước khi bắt đầu, hãy tạo một môi trường ảo để quản lý các thư viện cần thiết cho dự án.

  1. Cài đặt pythonvenv :

    Kiểm tra xem Python đã cài đặt venv chưa, nếu chưa hãy cài đặt:

    sudo apt update
    sudo apt install python3-venv python3 python3-pip
    

2.2 Thêm Subdomain vào tệp cấu hình Nginx

Phần này mình có làm ở series trước đó các bạn có thể vào đây để xem thêm.

  1. Tạo thư mục gốc cho tên miền của bạn

    Sử dụng lệnh sau để tạo thư mục gốc cho your_domain. Tùy chọn -p đảm bảo rằng mọi thư mục con cần thiết sẽ được tạo tự động:

    sudo mkdir -p /var/www/your_domain
    
  2. Phân quyền sở hữu thư mục

    Để đảm bảo người dùng hiện tại có quyền quản lý toàn bộ nội dung trong thư mục, bạn cần thay đổi quyền sở hữu bằng cách sử dụng biến $USER (đại diện cho người dùng hiện tại):

    sudo chown -R $USER:$USER /var/www/your_domain
    
  3. Thiết lập quyền truy cập

    Cuối cùng, đặt quyền truy cập cho thư mục là 755. Điều này cho phép:

    • Chủ sở hữu thư mục: Có đầy đủ quyền đọc, ghi, và thực thi.
    • Nhóm và người dùng khác: Chỉ có quyền đọc và thực thi (không thể sửa đổi nội dung).
    sudo chmod -R 755 /var/www/your_domain
    
  4. Tiếp theo tạo 1 file index.html dùng trình soạn nano

    nano /var/www/your_domain/index.html
    

    Bên trong thẻ:

    <html>
        <head>
            <title>Welcome to your_domain!</title>
        </head>
        <body>
            <h1>Hello world your_domain</h1>
        </body>
    </html>
    

    Lưu tệp bằng Ctrl+XYEnter .

  5. Cấu Hình Server Riêng Cho Subdomain

    Tiếp theo, tạo file cấu hình cho subdomain trong thư mục sites-available.

    • Mở file cấu hình bằng lệnh:

      sudo nano /etc/nginx/sites-available/your_domain
      
  6. Thêm Cấu Hình Cơ Bản Cho Nginx

    • Thêm nội dung sau vào file cấu hình, thay thế your_domain bằng subdomain hoặc tên miền của bạn:

      server {
              listen 80;
              listen [::]:80;
      
              root /var/www/your_domain;
              index index.html index.htm index.nginx-debian.html;
      
              server_name your_domain www.your_domain;
      
              location / {
                      try_files $uri $uri/ =404;
              }
      }
      
  7. Kích Hoạt Cấu Hình Cho Subdomain

    • Để kích hoạt cấu hình, tạo một liên kết tượng trưng (symbolic link) từ sites-available sang sites-enabled:
    sudo ln -s /etc/nginx/sites-available/your_domain /etc/nginx/sites-enabled/
    

    Hai khối máy chủ hiện được kích hoạt và cấu hình để phản hồi các yêu cầu dựa trên các chỉ thị listenvà server_name(bạn có thể đọc thêm về cách Nginx xử lý các chỉ thị này tại đây ):

    • your_domain : Sẽ trả lời các yêu cầu về your_domainwww.your_domain .
    • default: Sẽ phản hồi mọi yêu cầu trên cổng 80 không khớp với hai khối còn lại.
  8. Vấn Đề Hash Bucket Trong Nginx Khi bạn thêm một subdomain với tên miền dài hoặc nhiều subdomain vào cấu hình Nginx, có thể gặp lỗi liên quan đến server_names_hash_bucket_size. Lỗi này thường xuất hiện khi khởi động lại Nginx, với thông báo:

    [emerg] could not build server_names_hash, you should increase server_names_hash_bucket_size
    

    Cách Khắc Phục:

    1. Mở Tệp Cấu Hình Nginx Chính (nginx.conf)

      Sử dụng trình soạn thảo nano để mở file cấu hình:

      sudo nano /etc/nginx/nginx.conf
      
    2. Tìm Chỉ Thị server_names_hash_bucket_size

      Nhấn Ctrl+W và nhập từ khóa server_names_hash_bucket_size để tìm dòng này.

      Nếu dòng bị chú thích (có ký hiệu # phía trước), hãy xóa ký hiệu để kích hoạt.

      ...
      http {
          ...
          server_names_hash_bucket_size 64;
          ...
      }
      ...
      

      Lưu cấu hình Ctrl+XYEnter.

  9. Kiểm Tra Cấu Hình Nginx

    • Sau khi hoàn tất cấu hình, kiểm tra xem có lỗi cú pháp nào trong file Nginx không:
    sudo nginx -t
    
  10. Khởi Động Lại Nginx

    • Áp dụng thay đổi bằng cách khởi động lại Nginx:

      sudo systemctl restart nginx
      

    Sau khi hoàn tất hãy thử kiểm tra http://yourdomain (VD: blog.example.online) Nếu thấy giao diện như sau bạn đã thành công cài đặt cấu hình Subdomain.

2.3 Cài Môi Trường Ảo

  1. Khởi tạo môi trường ảo: Môi trường ảo giúp cô lập các thư viện và phiên bản Python cần thiết cho dự án, tránh xung đột với các dự án khác trên VPS.

    cd /var/www/your_domain
    python3 -m venv venv
    
  2. Kích hoạt môi trường ảo: Sau khi tạo môi trường ảo, hãy kích hoạt nó để sử dụng trong suốt quá trình cài đặt và chạy dự án:

    source venv/bin/activate
    

Lưu ý: Khi kích hoạt thành công, bạn sẽ thấy tên môi trường (ví dụ: (venv)) xuất hiện ở đầu dòng lệnh, như hình minh họa bên dưới.

Từ đây, bạn đang làm việc trong một môi trường độc lập, nơi các gói cài đặt sẽ không ảnh hưởng đến hệ thống chính.

3. Triển khai Ứng dụng AI

3.1 Cấu trúc dự án

Cấu trúc dự án cơ bản cho ứng dụng AI triển khai API sẽ như sau:

your_domain
│
├── app/
│   ├── main.py        # Tệp chính chứa API logic
│   └── detect.py      # Module xử lý dự đoán AI
├── models/
│   └── your_model.pt  # Tệp mô hình AI
├── templates/
│   └── index.html     # Giao diện HTML
├── static/
│   ├── uploaded       # Lưu trữ ảnh tải lên
│   └── predict        # Lưu trữ kết quả dự đoán
├── requirements.txt   # Danh sách thư viện cần thiết
└── venv/              # Môi trường ảo Python

3.2. Cài đặt các thư viện cần thiết

3.2.1. Tạo tệp requirements.txt

  1. Di chuyển đến thư mục dự án:

    cd /var/www/your_domain
    
  2. Tạo tệp requirements.txt:

    nano requirements.txt
    
  3. Thêm nội dung sau:

    fastapi
    uvicorn
    ultralytics
    python-multipart
    
  4. Lưu tệp bằng tổ hợp phím Ctrl+XYEnter.


3.2.2. Cài đặt thư viện từ requirements.txt

Khi đã kích hoạt môi trường ảo (source venv/bin/activate), tải các thư viện bằng lệnh:

pip install -r requirements.txt

Lệnh này sẽ tự động cài đặt tất cả các gói đã được chỉ định trong tệp requirements.txt.

3.3. Tạo giao diện HTML cơ bản

  1. Tạo thư mục lưu trữ các file HTML:

    mkdir templates
    
  2. Tạo tệp index.html:

    nano templates/index.html
    
  3. Nội dung file:

    <html>
        <head>
            <title>AI Deployment</title>
        </head>
        <body>
            <h1>Chào mừng đến với ứng dụng AI</h1>
            <p>Tải lên hình ảnh để bắt đầu:</p>
            <form action="/predict" method="post" enctype="multipart/form-data">
                <input type="file" name="file" accept="image/*">
                <button type="submit">Upload</button>
            </form>
        </body>
    </html>
    

3.4. Cấu hình Nginx

  1. Mở file cấu hình Nginx cho domain:

    sudo nano /etc/nginx/sites-available/your_domain
    
  2. Thêm cấu hình để Nginx chuyển tiếp các yêu cầu đến ứng dụng FastAPI:

    server {
        listen 80;
        listen [::]:80;
    
        root /var/www/your_domain;
        index index.html index.htm index.nginx-debian.html;
    
        server_name your_domain; 
    
        location / {
            proxy_pass http://127.0.0.1:8000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_redirect off;
        }
    }
    

    server_name blog.example.online , blog: Đây là subdomain (tên miền con) của example.online. Nó thường được sử dụng để tạo các trang web con dưới một tên miền chính. Bộ series trước mình có chia sẻ kỉ về phần này các bạn có thể xem tại đây.

  3. Khởi động lại Nginx để áp dụng thay đổi:

    sudo systemctl restart nginx
    

3.5. Mở cổng tường lửa (UFW)

  1. Kiểm tra trạng thái tường lửa:

    sudo ufw status
    

    Nếu tường lửa đang active, bạn cần mở cổng 8000.

  2. Mở cổng 8000:

    sudo ufw allow 8000
    
  3. Kiểm tra lại trạng thái tường lửa:

    sudo ufw status
    

    Kết quả sẽ hiển thị:

    8000                       ALLOW       Anywhere
    

3.6. Tạo API cơ bản với FastAPI

  1. Tạo thư mục app và tệp main.py:

    mkdir app
    nano app/main.py
    
  2. Thêm mã nguồn:

    from fastapi import FastAPI
    from fastapi.responses import HTMLResponse
    
    app = FastAPI()
    
    @app.get("/", response_class=HTMLResponse)
    async def read_root():
        with open("templates/index.html") as f:
            return HTMLResponse(content=f.read())
    
    if __name__ == "__main__":
        import uvicorn
        uvicorn.run(app, host="0.0.0.0", port=8000)
    
  3. Chạy ứng dụng FastAPI:

    python3 app/main.py
    
    example output
    
    (venv) namhost@server-host-839:/var/www/aitutorial$ python app/main.py 
    INFO:     Started server process [3491]
    INFO:     Waiting for application startup.
    INFO:     Application startup complete.
    INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
    
  4. Mở trình duyệt và truy cập http://your_domain.

    Truy cập địa chỉ http://your_domain từ trình duyệt. Nếu giao diện hiện lên, bạn đã cấu hình thành công.

    Khi bạn đã triển khai thành công API và cấu hình Nginx, bước tiếp theo là phát triển các chức năng cụ thể của ứng dụng AI. Dưới đây là hướng dẫn để bắt đầu:

4. Code các chức năng chính

4.1 Xử lý file tải lên

Thêm endpoint trong main.py để xử lý file tải lên từ form HTML.

  1. Cập nhật main.py:

    from fastapi import FastAPI, File, UploadFile
    from fastapi.responses import HTMLResponse
    import shutil
    import os
    
    app = FastAPI()
    
    @app.get("/", response_class=HTMLResponse)
    async def read_root():
        with open("templates/index.html") as f:
            return HTMLResponse(content=f.read())
    
    @app.post("/upload/")
    async def upload_image(file: UploadFile = File(...)):
        """Xử lý ảnh upload và trả về kết quả dự đoán"""
        upload_dir = "static/uploaded"
        predict_dir = "static/predict"
        os.makedirs(upload_dir, exist_ok=True)
        os.makedirs(predict_dir, exist_ok=True)
    
        file_path = os.path.join(upload_dir, file.filename)
        with open(file_path, "wb") as buffer:
            shutil.copyfileobj(file.file, buffer)
    
    if __name__ == "__main__":
        import uvicorn
        uvicorn.run(app, host="0.0.0.0", port=8000)
    
  2. Cập nhật giao diện index.html:

    Thay đổi action trong form HTML để trỏ đến endpoint /upload:

    <form action="/upload" method="post" enctype="multipart/form-data">
        <input type="file" name="file" accept="image/*">
        <button type="submit">Upload</button>
    </form>
    

    Reload lại trang và kiểm tra thử bạn hãy chọn 1 file ảnh và nhấn upload, nếu thành công sẽ trả về như sau.

    {"filename":"example.png","status":"File uploaded successfully"}
    

    Hoặc bạn có thể kiểm tra trong thư mục status/uploaded

    ls -l static/uploaded
    

4.2 Tạo chức năng Detect :

  1. Tạo tệp detect trong thư mục app :

    nano app/detect
    
  2. Thêm nội dung như sau:

    from ultralytics import YOLO
    import os
    import uuid
    
    class ModelHandler:
        def __init__(self, model_path):
            """
            Khởi tạo và tải mô hình AI.
            
            Args:
                model_path (str): Đường dẫn đến file model
            """
            self.model = YOLO(model_path)
    
        def predict(self, image_path, save_dir="static/predict"):
            """
            Dự đoán trên ảnh đầu vào và lưu kết quả.
    
            Args:
                image_path (str): Đường dẫn tới ảnh đầu vào.
                save_dir (str): Thư mục để lưu kết quả dự đoán.
    
            Returns:
                dict: Thông tin dự đoán bao gồm đường dẫn ảnh và kết quả dự đoán
            """
            # Thực hiện dự đoán
            results = self.model.predict(image_path)[0]
            
            # Đường dẫn tới file ảnh gốc
            uploaded_path = os.path.join('static/uploaded', os.path.basename(image_path))
            
            predictions = []
            for box in results.boxes:
                predictions.append({
                    "label": int(box.cls[0]),
                    "confidence": float(box.conf[0])
                })
            
            # Lưu kết quả
            saved_filename = f"{uuid.uuid4()}.jpg"
            output_path = os.path.join(save_dir, saved_filename)
            results.save(output_path)
    
            return {
                "uploaded_path": uploaded_path,
                "output_path": output_path,
                "predictions": predictions
            }
    
  3. Lưu tệp bằng tổ hợp phím Ctrl+XYEnter.

4.3 Thêm Endpoint Detect trong main.py:

  1. Mở file main.py và thêm endpoint để xử lý dự đoán.

  2. Import module detect.py để thực hiện quá trình phát hiện, nhập và gọi hàm từ module này.

    from fastapi import FastAPI, File, UploadFile
    from fastapi.responses import HTMLResponse
    from detect import ModelHandler #add module
    import shutil
    import os
    
    app = FastAPI()
    
    model = ModelHandler('models/yolov8n.pt')
    
    @app.get("/", response_class=HTMLResponse)
    async def read_root():
        with open("templates/index.html") as f:
            return HTMLResponse(content=f.read())
    
    @app.post("/upload/")
    async def upload_image(file: UploadFile = File(...)):
        """Xử lý ảnh upload và trả về kết quả dự đoán"""
        upload_dir = "static/uploaded"
        predict_dir = "static/predict"
        os.makedirs(upload_dir, exist_ok=True)
        os.makedirs(predict_dir, exist_ok=True)
    
        file_path = os.path.join(upload_dir, file.filename)
        with open(file_path, "wb") as buffer:
            shutil.copyfileobj(file.file, buffer)
    
        results = model.predict(file_path)
        
        return {
            "uploaded_image": results["uploaded_path"],
            "predicted_image": results["output_path"],
            "predictions": results["predictions"]
        }
    
    if __name__ == "__main__":
        import uvicorn
        uvicorn.run(app, host="0.0.0.0", port=8000)
    

    Reload lại trang và kiểm tra thử bạn hãy chọn 1 file ảnh và nhấn upload, nếu thành công sẽ trả về như sau.

    {
    	"uploaded_image":"static/uploaded/example.jpg",
    	"predicted_image":"static/predict/a1ea8ea2-ff18-43dc-9b80-a3fc03eb4b6d.jpg",
    	"predictions":[
    		{"label":16,"confidence":0.821751594543457},
    		{"label":15,"confidence":0.6605826020240784}
    	]
    }
    

    Hoặc bạn có thể kiểm tra trong thư mục status/predict

    ls -l status/predict
    

4.4 Cập nhật index.html

Cập nhật giao diện để hiển thị ảnh đã upload và ảnh sau khi dự đoán. Đồng thời, thêm logic để xử lý kết quả trả về từ API.

1. Sửa file index.html:

Mở tệp templates/index.html :

nano templates/index.html

Thêm sửa nội dung như sau:

<html>
    <head>
        <title>AI Deployment</title>
    </head>
    <body>
        <h1>Welcome to the AI Application</h1>
        <p>Upload an image below to get started:</p>
        <form id="uploadForm" method="post" enctype="multipart/form-data">
            <input type="file" name="file" accept="image/*" id="fileInput">
            <button type="submit">Upload</button>
        </form>

        <div class="container" id="resultContainer" style="display:none;">
            <h2>Uploaded Image</h2>
            <img id="uploadedImage" src="" alt="Uploaded Image">
            <h2>Prediction Result</h2>
            <img id="predictedImage" src="" alt="Predicted Image">
        </div>
    
        <script>
            const form = document.getElementById('uploadForm');
            const resultContainer = document.getElementById('resultContainer');
            const uploadedImage = document.getElementById('uploadedImage');
            const predictedImage = document.getElementById('predictedImage');
    
            form.addEventListener("submit", async (event) => {
                event.preventDefault();
                const formData = new FormData(form);
    
                try {
                    const response = await fetch('/upload/', {
                        method: 'POST',
                        body: formData
                    });

                    if (!response.ok) {
                        throw new Error(`HTTP error! Status: ${response.status}`);
                    }

                    const data = await response.json();

                    // Hiển thị ảnh đã upload và kết quả
                    uploadedImage.src = "/" + data.uploaded_image;
                    predictedImage.src = "/" + data.predicted_image;
                    resultContainer.style.display = "block";
                } catch (error) {
                    console.error("Error uploading file:", error);
                    alert("Failed to upload or process the image. Please try again.");
                }
            });
        </script>
    </body>
</html>

5. Kết quả

6. Nguồn tham khảo

7. Lời cảm ơn

🎉 Lời cảm ơn chân thành! 🎉

Cảm ơn các bạn đã dành thời gian quan tâm và chờ đợi series "Triển Khai Mô Hình AI Lên Web". Sự ủng hộ và đồng hành của các bạn là nguồn động lực lớn nhất để mình tiếp tục chia sẻ những kiến thức hữu ích và thú vị.

Hy vọng series này sẽ mang lại giá trị thực tế, giúp các bạn áp dụng AI vào các dự án của mình một cách dễ dàng hơn. Nếu bạn có bất kỳ góp ý hay câu hỏi nào, đừng ngần ngại chia sẻ – mình rất mong nhận được phản hồi từ các bạn!

Chúc các bạn luôn học tập hiệu quả và thành công trong hành trình khám phá trí tuệ nhân tạo! 🚀

Cùng nhau phát triển,

Tran Nam


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí