มีความแตกต่างอย่างมากระหว่างการสร้างแบบจำลองแมชชีนเลิร์นนิงที่ทำงานบนคอมพิวเตอร์ของคุณ กับการทำให้โมเดลนั้นพร้อมให้ผู้อื่นนำไปใช้ได้ หากใช้งานไม่ดี ผู้ใช้จะรู้สึกหงุดหงิดที่ซอฟต์แวร์ของคุณไม่น่าเชื่อถือ และอาจต้องใช้เวลาเป็นเดือนกว่าจะสำเร็จ!

บทความก่อนหน้านี้ของฉันได้แบ่งปันหลักการที่ฉันได้เรียนรู้ตลอดหลายปีที่ผ่านมา อันนี้อธิบายโค้ดตัวอย่างและวิจารณ์แนวทาง เมื่อฉันเริ่มปรับใช้โมเดลการเรียนรู้ของเครื่องในบริการบนเว็บ ฉันพบว่าความซับซ้อนนั้นน่าสับสน และฉันหวังว่าคำแนะนำนี้จะช่วยผู้อ่านในสถานการณ์ที่คล้ายคลึงกัน

ฉันสร้างชุดต้นแบบขึ้นมาเพื่อสำรวจเครื่องมือและแนวคิดต่างๆ ใน ​​MLOps สำหรับตัวแยกประเภทข้อความพื้นฐาน โพสต์นี้จะเน้นไปที่ต้นแบบล่าสุด มีการฝึกอบรม การทดสอบ และการปรับใช้โมเดลอัตโนมัติกับบริการเว็บด้วย AWS Lambda

ฉันจำกัดจำนวนเครื่องมือเฉพาะของ ML ด้วยเหตุผลสองประการ: 1) ฉันต้องการใช้โครงสร้างพื้นฐานที่คล้ายกันกับโครงการซอฟต์แวร์แบบดั้งเดิม และ 2) ในที่ทำงาน มักจะง่ายกว่าในการตอบสนองความต้องการด้านการปฏิบัติตามข้อกำหนดด้วยวิธีนี้

หมายเหตุด้านข้าง: คุณอาจสังเกตเห็นว่า repos เหล่านี้บางส่วนมีอายุสองสามปี เดิมทีฉันทำสิ่งเหล่านี้เพื่อตัวเองเท่านั้น หลายปีที่ผ่านมา ฉันพบว่าฉันมักจะใช้สิ่งเหล่านี้เป็นตัวอย่างสำหรับคนอื่นๆ และนั่นเป็นแรงบันดาลใจให้ฉันเขียนมันขึ้นมาในที่สุด

พื้นฐาน

บางครั้งฉันก็คิดถึงวันเก่าๆ เมื่อคุณโฮสต์โค้ดของคุณบนเซิร์ฟเวอร์ใต้โต๊ะที่ไหนสักแห่ง! ในสมัยนั้น การรับโค้ดหรือโมเดลของเราบนเซิร์ฟเวอร์หมายถึงการคัดลอกไฟล์และรีสตาร์ท web daemon

มีปัญหามากมายเกี่ยวกับวิธีการดังกล่าว นี่เป็นปัญหาบางประการ:

  • หมายความว่าเราจะต้องใช้เวลามากขึ้นในการจัดการเซิร์ฟเวอร์ เช่น การใช้แพตช์รักษาความปลอดภัย รับรองว่าอินเทอร์เน็ตและพลังงานจะไม่ดับ หรือแก้ไขเมื่อส่วนประกอบฮาร์ดแวร์เสียหาย
  • กระบวนการนี้มีแนวโน้มที่จะเกิดข้อผิดพลาดและจุดบกพร่อง เช่น รหัสที่ทำงานบนคอมพิวเตอร์ของคุณ แต่ไม่ใช่ในเซิร์ฟเวอร์ หรือคุณอาจลืมทำการทดสอบ
  • บริการใต้โต๊ะสามารถรองรับคำขอจำนวนมากต่อวินาทีเท่านั้น หากเว็บไซต์ของคุณได้รับความนิยมอย่างกะทันหัน ก็อาจไม่สามารถตามทันจำนวนผู้ใช้ที่เพิ่มขึ้นได้

เครื่องมือสมัยใหม่ได้รับการออกแบบมาเพื่อแก้ไขปัญหาเหล่านี้ แต่อาจทำให้การเรียนรู้ฐานโค้ดใหม่ยากขึ้น

การปรับใช้โมเดลกับ Lambda

ซื้อคืนนี้ มีการฝึกอบรมและให้บริการสำหรับโมเดลการจัดหมวดหมู่ข่าว scikit-learn แต่ควรใช้ได้กับ PyTorch และไลบรารีอื่น ๆ

สมมติว่าเรากำลังสร้างบริการแบ็กเอนด์สำหรับสื่อเพื่อแนะนำหมวดหมู่ที่เหมาะสมสำหรับโพสต์ใหม่ ส่วนหน้าขนาดกลางจะส่งข้อความของบทความไปยังส่วนหลังของเราและรับหมวดหมู่ที่คาดการณ์ไว้

ขณะที่ฉันดู repo ฉันจะสรุปคำศัพท์ในตอนท้ายของแต่ละส่วน

API เกตเวย์และแลมบ์ดา

API เกตเวย์ได้รับคำขอ HTTP และส่งต่อไปยัง Lambda หากมีผู้ปฏิบัติงาน Lambda ว่าง ระบบจะส่งต่อคำขอไปยังผู้ปฏิบัติงาน หากไม่มี ระบบจะเริ่มต้นผู้ปฏิบัติงานใหม่ซึ่งจะโหลดโค้ด โหลดโมเดล และดำเนินการตามคำขอ หากใช้เวลาโหลดโค้ดและโมเดลนานกว่า 30 วินาที คำขอจะล้มเหลว แต่ในที่สุดจะบูต จากนั้นคำขอก็จะทำงาน

สำหรับการทดสอบ ฉันใช้คำขอ POST พร้อมเพย์โหลดแบบง่ายดังนี้:

{
    "text": "The Seattle Seahawks crushed the Cleveland Browns on Thursday 52-7"
}

เกตเวย์ API รับคำขอและเรียกใช้ตัวจัดการ Lambda ของเราพร้อมข้อมูลคำขอในพารามิเตอร์

ในโค้ด Lambda นั้น API เกตเวย์จะส่งข้อมูลนี้ใน เหตุการณ์ และ บริบทสองคำสั่ง จากให้บริการ/app/app.py:

def lambda_handler(event, context):
    request_body = json.loads(event["body"])
    prediction = model.predict([request_body["text"]])[0]

    return {
        "statusCode": 200,
        "body": json.dumps({
            "response": prediction,
            "request": request_body
        })
    }

เกตเวย์ API จะส่งเนื้อหาคำขอ POST ในเหตุการณ์ ["body"] เป็นสตริงธรรมดา ดังนั้นเราจึงจำเป็นต้องแยกวิเคราะห์ JSON ในนั้น จากนั้นเราจะรันฟิลด์ข้อความผ่านโมเดล โปรดทราบว่าเราจำเป็นต้องทำให้มันเป็นรายการอินพุต (ที่มีองค์ประกอบเดียว) จากนั้นจึงนำการทำนายแรกจากเอาต์พุต นั่นเป็นเพราะว่าวิธีการทำนายแบบ scikit-learn ได้รับการออกแบบมาสำหรับชุดข้อมูล จากนั้น API เกตเวย์ต้องการการตอบสนองในรูปแบบ dict โดยเฉพาะ รวมถึงรหัสสถานะ HTTP (200) ฉันชอบที่จะส่งคืนข้อมูลคำขอเพื่อให้การแก้ไขข้อบกพร่องง่ายขึ้น

คำศัพท์ใหม่

  • AWS Lambda: ซึ่งจะรันโค้ดของเราบนเซิร์ฟเวอร์และจัดการการขยายจำนวนพนักงานโดยอัตโนมัติ
  • AWS API Gateway: สิ่งนี้ทำหน้าที่เป็นสะพานเชื่อมระหว่าง Lambda ของเรากับคำขอที่มาจากอินเทอร์เน็ต
  • HTTP POST: นี่คือ "วิธี HTTP" ที่เราใช้อยู่ แม้ว่า GET จะเป็นวิธีการที่เหมาะสมทางความหมายมากกว่า แต่โดยทั่วไปแล้ว GET จะไม่รองรับการส่ง JSON ประสบการณ์ของฉันเป็นเรื่องธรรมดามากกว่า POST เนื่องจากโครงสร้างข้อมูลอินพุตมักจะซับซ้อน

Docker และ Python เรียกโดย Lambda

จากการให้บริการ/แอป/Dockerfile:

CMD ["app.lambda_handler"]

นี่คือบรรทัดสุดท้ายของ Dockerfile และบอกให้ Lambda เรียกใช้ฟังก์ชัน lambda_handler ใน app.py

เมื่อ Lambda เริ่มต้น มันจะนำเข้าไฟล์ app.py ซึ่งทำให้ทุกอย่างที่อยู่นอกฟังก์ชันถูกรัน เช่น การโหลดโมเดลในกรณีของเรา สำหรับ Lambdas เราเรียกสิ่งนี้ว่าการเริ่มต้นอย่างเย็นชา จากนั้นจะพร้อมใช้งานจนกว่าพนักงานจะปิดตัวลง โมเดลถูกสร้างลงในอิมเมจ Docker เดียวกันกับโค้ด และโหลดเหมือนกับไฟล์อื่นๆ อิมเมจ Docker ถูกสร้างและปรับใช้จาก Github Actions เมื่อมีการรวมโค้ดเข้ากับ main ซึ่งเปลี่ยนแปลงทุกอย่างในโฟลเดอร์ serving ในทำนองเดียวกัน การกำหนดค่าเกตเวย์ API และ Lambda ก็สามารถอัปเดตได้ในเวลาเดียวกัน ไฟล์หนึ่งใน ให้บริการ คือไฟล์โมเดลซึ่งจัดเก็บโดยใช้ data version control (DVC) หากโมเดลเปลี่ยนแปลงเมื่อรวมเข้ากับ main อิมเมจ Docker จะถูกสร้างขึ้นใหม่และปรับใช้กับโมเดลใหม่อีกครั้ง

นี่คือตำแหน่งที่โหลดโมเดลในเสิร์ฟ/app/app.py โดยละเว้นเวลาและเมตริก:

def load_model():
    ...
    model_path = os.path.join(os.path.dirname(__file__), "data/model.joblib.gz", )
    model = joblib.load(model_path)

    ...

    return model

model = load_model()

สิ่งที่เรากำลังทำคือเรียก joblib.load บนพาธของไฟล์ วิธีนี้ใช้ได้เนื่องจากโมเดลถูกบันทึกด้วย joblib และเนื่องจากโค้ดที่ให้บริการมีการขึ้นต่อกันทั้งหมดที่จำเป็นสำหรับโมเดลจาก scikit-learn

บรรทัดแรกคือการทำให้แน่ใจว่าเส้นทางของไฟล์สัมพันธ์กับ app.py ไม่สัมพันธ์กับตำแหน่งที่ app.py เรียกใช้อยู่ เพราะฉันไม่รู้ว่า Lambda ทำให้สิ่งนั้นสอดคล้องกันหรือไม่ ฉันทำอย่างนั้นจนเป็นนิสัยเพราะฉันเคยถูกเผาไหม้ในอดีตเมื่อมีการเรียกใช้โค้ดจากไดเร็กทอรีที่ไม่คาดคิด

ไฟล์โมเดลนั้นถูกอบเข้าไปในอิมเมจ Docker ในเสิร์ฟ/แอพ/Dockerfile:

COPY data ./data

เป็นเพียงการคัดลอกโฟลเดอร์เสิร์ฟ/แอป/ข้อมูลลงในอิมเมจ Docker จากเครื่องที่สร้างอิมเมจ Docker หากเราต้องการโมเดลอื่นด้วยเหตุผลบางอย่าง เราก็จะใส่มันลงในโฟลเดอร์นั้น และมันจะพร้อมใช้งานภายใน Docker ด้วย

ข้อกำหนดใหม่

  • สตาร์ทเย็น: เมื่อไม่มีผู้ปฏิบัติงาน Lambda พร้อมให้บริการทันที Lambda จะต้องเริ่มต้นหนึ่งคน
  • นักเทียบท่า: นักเทียบท่าคือวิธีการรวมโค้ด ข้อมูล และการขึ้นต่อกันเข้าด้วยกันในลักษณะที่สามารถเรียกใช้ในลักษณะเดียวกันบนคอมพิวเตอร์ทุกเครื่อง
  • การควบคุมเวอร์ชันข้อมูล (DVC): ฉันใช้ DVC เพื่อกำหนดเวอร์ชันของไฟล์ขนาดใหญ่ เช่น โมเดล เนื่องจาก git ทำงานได้ไม่ดีกับไฟล์ขนาดใหญ่
  • การดำเนินการ Github: Github ที่จะรันโค้ดของเราบนเซิร์ฟเวอร์ในช่วงเวลาสั้นๆ ซึ่งถูกกระตุ้นโดยการเปลี่ยนแปลงใน Github หรือตัวจับเวลา ฟรีสำหรับการใช้งานขนาดเล็ก
  • main branch: ในการพัฒนา Github เป็นเรื่องปกติที่จะใส่โค้ดเวอร์ชันปัจจุบันไว้ในสาขาที่ชื่อ “main” และสร้างสาขาอื่นในขณะที่เขียนโค้ดใหม่
  • รวม: เมื่อการเปลี่ยนแปลงจากสาขา git หนึ่งถูกรวมเข้ากับสาขาอื่น ในกรณีนี้ ฉันกำลังพูดถึงการรวมโค้ดจากสาขาการพัฒนาเข้ากับสาขาหลัก

การอ่านเพิ่มเติม

การสร้างและการปรับใช้ด้วย CDK จาก Github Actions

อิมเมจ Docker ถูกสร้างขึ้นและอัปโหลดไปยัง AWS Elastic Container Registry (ECR) ใน CDK ในการให้บริการ/ปรับใช้/stacks/lambda_service.py:

handler = lambda_.DockerImageFunction(
    self,
    "ExampleTextClassifierHandler",
    code=lambda_.DockerImageCode.from_image_asset("../app"),
    timeout=cdk.Duration.seconds(60),
    memory_size=3008
)

ฟังก์ชัน from_image_asset สร้างอิมเมจ Docker ให้เรา ซึ่งใช้เป็นพารามิเตอร์ของไดเร็กทอรีที่มี Dockerfile ที่คุณต้องการสร้าง ฉันกำลังตั้งค่าขนาดหน่วยความจำเป็น 3 GB ซึ่งอาจเกินความจำเป็นอย่างเคร่งครัด แต่จะช่วยเรื่องเวลาแฝงได้ เนื่องจาก Lambda ปรับขนาด CPU ด้วย RAM ที่มากขึ้น การหมดเวลา 60 วินาทีจะอยู่ที่เลเยอร์ Lambda โปรดทราบว่าเกตเวย์ API มีระยะหมดเวลาอิสระ โดยสูงสุด 30 วินาที

อิมเมจ Docker จำเป็นสำหรับ Lambda เนื่องจากขนาดของโมเดลและขนาดการพึ่งพา Python ไม่เช่นนั้น ฉันคงจะใช้ไฟล์ zip ของ Lambda ซึ่งเร็วกว่า

CDK เป็นเฟรมเวิร์กสำหรับโครงสร้างพื้นฐานในรูปแบบโค้ด (IaC) ซึ่งหมายความว่าเรากำลังกำหนดการกำหนดค่า AWS ของเราในโค้ด ช่วยให้เราสามารถกำหนดมาตรฐานและการปรับใช้ซึ่งช่วยลดอุบัติเหตุได้ เนื่องจากมันถูกเก็บไว้ใน repo จึงหมายความว่าเราสามารถใช้กระบวนการคำขอดึงเพื่อให้มีการตรวจสอบโดยผู้ทรงคุณวุฒิสำหรับการกำหนดค่าของเรา นอกจากนี้ยังหมายความว่าเรากำลังอัปเดตรหัสบริการและโครงสร้างพื้นฐานของเราไปพร้อมๆ กัน ดังนั้นเราจึงสามารถรวมโมเดลใหม่ที่มีหน่วยความจำเพิ่มขึ้นเข้าด้วยกันได้ เป็นต้น และยังหมายความว่าเราสามารถคืนค่าการเปลี่ยนแปลงการกำหนดค่าได้ง่ายขึ้น นอกจากนี้ เพื่อประโยชน์สุดท้ายประการหนึ่ง CDK จะถูกนำไปใช้เพื่อให้การปรับใช้ส่วนใหญ่เสร็จสิ้นโดยมีการหยุดทำงานเพียงเล็กน้อยหรือไม่มีเลย

สแต็ก CDK รันจาก .github/workflows/deploy_service.yml:

    - run: cdk deploy --require-approval never
      env:
        AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        AWS_DEFAULT_REGION: us-west-2

สิ่งนี้จะรันการปรับใช้ cdk เราต้องปิดคำถามการอนุมัติแบบโต้ตอบ ไม่เช่นนั้นคำถามจะหยุดทำงาน ส่วนอื่นๆ คือการตั้งค่าตัวแปรสภาพแวดล้อมสำหรับผู้ใช้ AWS IAM ซึ่งคัดลอกมาจากความลับการดำเนินการ Github ที่ตั้งค่าไว้ใน repo นี้ (ฉันตั้งค่าเหล่านั้นด้วยตนเอง) จากนั้นเมื่อ CDK สร้างเกตเวย์ API ของคุณ ก็จะแสดง URL ที่สร้างขึ้นอัตโนมัติใน Github Actions

ยิ่งไปกว่านั้นใน Deploy_service.yaml เราติดตั้ง DVC และดึงโมเดล:

    - run: pip install dvc[s3]
    - run: dvc pull
      env:
        AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

นอกจากนี้ยังต้องมีข้อมูลรับรอง AWS เพื่ออ่านจากบัคเก็ต S3

ที่ด้านบนสุดของไฟล์ เราจะเห็นได้ว่าเมื่อใดที่เวิร์กโฟลว์ Github Action ถูกทริกเกอร์:

on:
  push:
    branches:
      - main
    paths:
    - 'serving/**'
    - '.github/workflows/deploy_service.yml'

สิ่งนี้จะเรียกใช้การกระทำของ release_service.yaml เมื่อมีการอัปเดต main (ซึ่งเกิดขึ้นในการรวม PR) และเมื่อมีการเปลี่ยนแปลงสิ่งใดภายใต้การให้บริการ/ หรือปรับใช้กับบริการ.yaml เอง

ข้อกำหนดใหม่

  • AWS Elastic Container Registry (ECR): ที่สำหรับอัปโหลดอิมเมจ Docker ใน AWS เพื่อใช้ใน Lambda และบริการของ AWS อื่นๆ
  • โครงสร้างพื้นฐานเป็นโค้ด (IaC): การกำหนดโครงสร้างพื้นฐานของคุณด้วยโค้ดแทนที่จะคลิกปุ่มในส่วนติดต่อผู้ใช้
  • AWS Cloud Development Kit (CDK): เครื่องมือ AWS และไลบรารีสำหรับโครงสร้างพื้นฐานเป็นโค้ดที่สามารถกำหนดได้ใน Javascript หรือ Python
  • AWS S3: พื้นที่จัดเก็บไฟล์ใน AWS
  • ผู้ใช้ AWS IAM: บัญชีสำหรับคอมพิวเตอร์หรือบุคคลที่สามารถดำเนินการในบัญชี AWS ของคุณตามสิทธิ์ที่เกี่ยวข้อง โดยทั่วไปจะเข้าถึงได้โดยใช้รหัสการเข้าถึงและความลับ รหัสการเข้าถึงและความลับจะคล้ายคลึงกับชื่อผู้ใช้และรหัสผ่าน แต่เป็นการเข้าถึงโดยทางโปรแกรม
  • ความลับของการดำเนินการ Github: การกำหนดค่าที่ละเอียดอ่อนซึ่งจัดเก็บไว้ใน Github ที่เชื่อมโยงกับ repo และพร้อมใช้งานสำหรับ Github Action เช่น รหัสการเข้าถึงหรือรหัสผ่าน
  • เวิร์กโฟลว์ Github Action: ชุดขั้นตอนเพื่อให้ Github Actions ทำงาน ซึ่งเชื่อมโยงกับทริกเกอร์ เช่น การเปลี่ยนแปลงสาขา มีไฟล์ yaml หนึ่งไฟล์ต่อเวิร์กโฟลว์
  • Pull request (PR): ใน Github เป็นเรื่องปกติที่จะดำเนินการพัฒนาในสาขาใหม่ จากนั้นจึงส่ง Pull Request (PR) ซึ่งเป็นคำขอเพื่อรวมโค้ดเข้ากับสาขาอื่น (โดยทั่วไปจะเป็นสาขาหลัก) เป็นเรื่องปกติที่เพื่อนร่วมงานจะตรวจสอบรหัสที่แก้ไขใน PR ก่อนที่จะอนุมัติหรือขอการเปลี่ยนแปลง เป็นเรื่องปกติที่บริษัทต่างๆ จะจำกัด repos Github ของตน เพื่อไม่ให้คำขอดึงไม่สามารถรวมเข้าด้วยกันได้จนกว่าจะมีคนอนุมัติ

การอ่านเพิ่มเติม

การทดสอบบริการอัตโนมัติทำงานจาก Github Actions

เราไม่ต้องการปรับใช้โค้ดที่ไม่ถูกต้อง ดังนั้นเราจึงมีการทดสอบอัตโนมัติด้วย

การทดสอบที่ฉันมีนั้นพื้นฐานมาก: 1) ทดสอบว่าจุดสิ้นสุดสามารถทำงานได้โดยไม่ขัดข้อง 2) ทดสอบว่าจุดสิ้นสุดขัดข้องตามที่คาดไว้หากไม่มีข้อความที่ป้อน สิ่งเหล่านั้นอยู่ในการเสิร์ฟ/ทดสอบ/test_lambda_handler.py:

class BasicTests(unittest.TestCase):
    def test_basic_path(self):
        lambda_handler({"body": json.dumps({"text": "example input"})}, None)

    def test_crash(self):
        self.assertRaises(BaseException, lambda_handler, {"body": json.dumps({"not_the_right_one": "example input"})}, None)

ในการทดสอบเหล่านี้ ฉันกำลังเรียก lambda_handler โดยตรงโดยไม่มีการตั้งค่า บริบท เนื่องจากฟังก์ชันไม่ได้ใช้มันอยู่แล้ว

สิ่งเหล่านี้ถูกทริกเกอร์จาก .github/workflows/test_service.yml:

      - name: Run tests
        run: make test-service

“make test-service” ถูกกำหนดไว้ใน Makefile ที่รากของ repo:

test-service:
   PYTHONPATH=serving/app/ python serving/tests/test_lambda_handler.py

นี่เป็นเพียงการรันคำสั่ง python เพื่อรันการทดสอบและตรวจสอบให้แน่ใจว่าสามารถนำโค้ดของแอปไปยังโค้ดทดสอบได้ ไม่จำเป็นต้องมีกฎ Makefile อย่างเคร่งครัด แต่ฉันชอบที่จะทำเช่นนั้นเพื่อที่ฉันจะรันคำสั่งทดสอบเดียวกันทุกที่: ทั้งใน Github Actions สำหรับการทดสอบอัตโนมัติและบนคอมพิวเตอร์ของฉันสำหรับการทดสอบในเครื่องด้วย

Github Action ถูกทริกเกอร์ที่ด้านบนของ test_service.yml:

on:
  push:
    branches-ignore:
      - main
    paths:
    - 'serving/**'
    - '.github/workflows/test_service.yml'

สิ่งนี้แปลเป็น "เรียกใช้การกระทำนี้ทุกครั้งที่มีการพุชโค้ดไปยังสาขายกเว้นสาขาหลักที่มีการเปลี่ยนแปลงภายใต้การให้บริการหรือมีโอกาสที่จะ test_service.yml" สิ่งนี้จะทำงานตามคำขอดึงและสาขาที่ไม่ใช่ PR Github ได้รับการกำหนดค่าเพื่อให้หากการทดสอบล้มเหลว ระบบจะบล็อก PR ใดๆ สำหรับสาขานั้น

การทดสอบจะดำเนินการหากมีสิ่งใดภายใต้ การให้บริการ เปลี่ยนแปลงในคำขอดึง รวมถึงการเปลี่ยนแปลงไฟล์โมเดลด้วย!

ฝึกโมเดลอีกครั้งจาก Github Actions

ตอนนี้เรามาดูกันว่าโมเดลได้รับการฝึกฝนอย่างไร ส่วนสำคัญของการฝึกอบรมอยู่ใน training/src/main.py:

model = make_pipeline(
    TfidfVectorizer(min_df=30, ngram_range=(1, 2), sublinear_tf=True),
    LogisticRegressionCV()
)

model.fit(training_data.data, training_data.target)

joblib.dump(model, os.path.join(args.output_dir, "model.joblib.gz"), compress=9)

นี่คือแบบจำลองบิ๊กแกรมที่ใช้การถดถอยโลจิสติกพร้อมการตรวจสอบข้ามเพื่อปรับน้ำหนักการทำให้เป็นมาตรฐานให้เหมาะสม การตั้งค่า min_df จะละเว้น ngrams ที่ไม่บ่อยนัก ซึ่งช่วยให้เรารักษาโมเดลให้มีขนาดเล็กได้โดยไม่สูญเสียความแม่นยำไปมากนัก Sublinear_tf ลดผลกระทบของการซ้ำของ ngram เดียวกัน และฉันพบว่านั่นทำให้โมเดลมีความแข็งแกร่งขึ้นเล็กน้อยเมื่อเทียบกับอินพุตแปลก ๆ

ฉันใช้ตัวช่วย make_pipeline จาก scikit-learn เพื่อสร้างวัตถุ Pipeline ฉันพบว่าไปป์ไลน์ scikit-Learn 1) ทำให้การโหลดและการบันทึกง่ายขึ้น 2) ทำให้การปรับแต่งไฮเปอร์พารามิเตอร์มีประสิทธิภาพมากขึ้น 3) ลดการรั่วไหลของข้อมูลการทดสอบโดยไม่ได้ตั้งใจในการฝึกอบรม

หากคุณดูที่ main.py คุณจะเห็นการประเมินโมเดลและการเปรียบเทียบกับข้อมูลพื้นฐานด้วย

main.py ถูกเรียกจาก Makefile จากคำสั่ง DVC:

train:
   dvc repro train

เช่นเคย ฉันชอบใช้ Makefile เพื่อให้แน่ใจว่าฉันใช้คำสั่งเดียวกันบนเครื่องของฉันและบนเซิร์ฟเวอร์ ในตัวอย่างนี้ เป็นการเรียก DVC ให้สร้างไปป์ไลน์ "train" ซึ่งกำหนดไว้ใน dvc.yaml:

stages:
  train:
    cmd: python training/src/main.py serving/app/data/ serving/app/requirements.txt
    deps:
    - training/
    outs:
    - serving/app/data/model.joblib.gz
    metrics:
    - serving/app/data/metrics.json

นี่คือ คุณสมบัติไปป์ไลน์ของ DVC และคล้ายกับ make ยกเว้นว่ามันจะรัน dvc add โดยอัตโนมัติในไฟล์เอาท์พุตของเราเพื่อให้แน่ใจว่าไฟล์เหล่านั้นถูกพุชไปที่ S3 ดัน dvc ส่วนเมตริกจะบันทึกเมตริกในไฟล์ที่ระบุ และคำสั่ง repro จะแสดงค่าที่อัปเดตให้เราทราบ

การฝึกอบรมถูกทริกเกอร์จาก Github Actions ใน .github/workflows/train_model.yml:

# build when a branch other than main changes training or this file:
on:
  push:
    branches-ignore:
      - main
    paths:
    - 'training/**'
    - '.github/workflows/train_model.yml'

jobs:
  train:
    runs-on: ubuntu-latest
    steps:
      # ... for full steps see github ...
      # train the model
      - name: Train model
        run: make train
      # run the web service tests to make sure it still works!
      - run: pip install -r serving/app/requirements.txt
      - name: Run web service tests
        run: make test-service
      # commit the model, which needs the IAM user to access S3 on dvc push
      - name: Commit model
        # email address from https://github.community/t/github-actions-bot-email-address/17204/5
        run: |
          git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git config --local user.name "github-actions[bot]"
          git commit -am "Automated model build"
          dvc push
          git push
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

ในตัวอย่างนี้ ฉันแสดงความคิดเห็นแบบอินไลน์และตัดโค้ดสำเร็จรูปบางส่วนออก ฉันกำลังเรียกใช้การทดสอบบริการเว็บ ก่อน ส่งมอบโมเดลให้กับ repo เนื่องจาก 1) การดำเนินการนี้ไม่สามารถทริกเกอร์การดำเนินการ test_service และ 2) ฉันไม่ต้องการส่งมอบโมเดลที่ไม่ผ่านการทดสอบ

การกำหนดค่า AWS สำหรับการดำเนินการ DVC และ Github

ฉันตั้งค่า DVC ให้ใช้ที่เก็บข้อมูล S3 สำหรับพื้นที่เก็บข้อมูล ซึ่งได้รับการจัดการโดยโค้ด Terraform ภายใต้โครงสร้างพื้นฐาน โดยจะสร้างบัคเก็ต S3 สำหรับ DVC เพื่อใช้รวมถึงผู้ใช้ IAM สำหรับ Github Actions เพื่อใช้ในการเข้าถึง DVC และปรับใช้ CDK

รหัสมีรายละเอียดเป็นบางส่วน แต่ฉันจะให้ภาพรวมของ Infrastructure/resources.tf พร้อมความคิดเห็นที่เพิ่ม:

// create the bucket. In this file it's referenced as aws_s3_bucket.b.bucket
resource "aws_s3_bucket" "b" {
  bucket_prefix = "trnka-dvc-"
  acl = "private"
}

// create the IAM user for Github Actions to use
resource "aws_iam_user" "github_actions" {
  name = "github_actions_lambda_ml"
  force_destroy = true
}

// give the IAM user permissions to list files in the bucket and read/write/delete files
resource "aws_iam_user_policy" "dvc_policy" {
  name = "dvc_policy"
  user = aws_iam_user.github_actions.name

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:ListBucket"],
      "Resource": ["arn:aws:s3:::${aws_s3_bucket.b.bucket}"]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "s3:DeleteObject"
      ],
      "Resource": ["arn:aws:s3:::${aws_s3_bucket.b.bucket}/*"]
    }
  ]
}
EOF
}

// this policy is long but ensures the IAM user can run CDK and upload ECR images
resource "aws_iam_user_policy" "cdk_policy" {
  name = "cdk_policy"
  user = aws_iam_user.github_actions.name

  // ...
}

// make sure there's an access key
resource "aws_iam_access_key" "github_actions" {
  user = aws_iam_user.github_actions.name
}

// when we run terraform apply, it'll show the secret on the command line
// which we can copy into Github Actions Secrets
output "secret" {
  value = aws_iam_access_key.github_actions.secret
}

โค้ดนี้สร้างบัคเก็ต S3 สำหรับ DVC ซึ่งเป็นผู้ใช้ IAM สำหรับ Github Actions ให้สิทธิ์ที่เหมาะสมแก่ผู้ใช้ IAM และสร้างคีย์การเข้าถึงเพื่อให้ Github Actions สามารถ “เข้าสู่ระบบ” เข้าสู่ AWS ได้

ข้อดีอย่างหนึ่งของโครงสร้างพื้นฐานที่เป็นโค้ดคือเราสามารถขอให้ผู้เชี่ยวชาญตรวจสอบก่อนที่จะรันได้

ข้อกำหนดใหม่

  • Terraform: Terraform เป็นเครื่องมือสำหรับโครงสร้างพื้นฐานเป็นโค้ดที่ทำงานร่วมกับผู้ให้บริการคลาวด์ยอดนิยมหลายราย
  • นโยบาย AWS IAM: ฉันอธิบายเรื่องนี้ได้ไม่เก่ง แต่เป็นรายการสิทธิ์ที่สามารถแนบกับผู้ใช้หรือบทบาท IAM ได้
  • บทบาท AWS IAM: สิ่งนี้เหมือนกับผู้ใช้ IAM แต่คุณไม่สามารถเข้าสู่ระบบได้ มีไว้เพื่อใช้บริการเท่านั้น หากคุณอายุพอๆ กับฉัน คุณอาจคิดว่านี่เป็นผู้ใช้บริการที่มีความปลอดภัยดีกว่า

สิ่งที่อาจจะผิดไป? คำติชม

การทบทวนงานของคุณเองเป็นเรื่องยาก แต่ฉันจะลองดูอย่างยุติธรรม! ฉันมีสามส่วน:

  1. การทบทวนหลักการทั่วไป
  2. ภาพรวมของความต้องการของอุตสาหกรรมที่ไม่ได้กล่าวถึงเป็นอย่างอื่น
  3. การอภิปรายลำดับความสำคัญที่ต้องแก้ไข

หลักการทั่วไป

บริการจัดการเวลาแฝงเทียบกับต้นทุนได้ดีหรือไม่

  • AWS Lambda ปรับขนาดขึ้นและลงโดยอัตโนมัติ ดังนั้นหากมีผู้ใช้เพิ่มขึ้นอย่างรวดเร็ว ระบบจะจัดการเรื่องนั้นแล้วลดขนาดลงเพื่อประหยัดค่าใช้จ่ายในภายหลัง ไม่จำเป็นต้องมีการกำหนดค่าพิเศษใด ๆ ในการดำเนินการนี้
  • เมื่อ Lambda ขยายขนาดและอุ่นเครื่อง “อินสแตนซ์” ใหม่ จะต้องดาวน์โหลดอิมเมจ Docker และโหลดโมเดล เราเรียกสิ่งนี้ว่าการเริ่มต้นอย่างเย็นชา หากรูปภาพและโมเดลมีขนาดใหญ่ปานกลาง คำขอ API จะหมดเวลาขณะโหลด สิ่งนี้แย่เป็นพิเศษใน repo ของเราเนื่องจากไม่มีผู้ใช้ที่ใช้งานอยู่ ดังนั้นมันจะอยู่ที่ 0 พร้อมกันจนกว่าจะมีการร้องขอในระหว่างการทดสอบ จากนั้นหมดเวลาขณะโหลดโมเดลจนกว่าจะมี Warm Lambda
  • ในหัวข้อเรื่องต้นทุน Lambda จะขยายขนาดตามความจำเป็นกับคำขอที่เข้ามาจนถึงขีดจำกัดการทำงานพร้อมกันทั่วทั้งภูมิภาค ดังนั้นจึงมีคนสามารถสแปม API ของคุณเพื่อขยายขนาด Lambda ของคุณและเพิ่มค่าบริการ AWS ของคุณได้อย่างมาก
  • repo ถูกนำไปใช้ทั้งหมดใน us-west-2 หากมีการเรียก API จากทั่วโลกหรือแม้แต่ในสหรัฐอเมริกา ผู้ใช้จำนวนมากจะได้รับการตอบสนองที่ช้าเพียงเพราะพวกเขาอยู่ห่างจากเซิร์ฟเวอร์ ในทางกลับกัน หากถูกเรียกโดยเซิร์ฟเวอร์หรือผู้ใช้ที่อยู่ใกล้ us-west-2 เท่านั้นก็ไม่เป็นไร

บริการนี้มีความพร้อมใช้งานสูงหรือไม่

  • เนื่องจาก Cold Start เมื่อใช้บริการจะทำให้เกิดการหยุดทำงานเต็มรูปแบบจนกว่าจะโหลดโมเดลใหม่ ในสถานการณ์ทางวิชาชีพซึ่งเป็นสิ่งที่ยอมรับไม่ได้แต่อาจเป็นที่ยอมรับได้สำหรับงานอดิเรก
  • เราจะสามารถย้อนกลับอย่างรวดเร็วและปลอดภัยได้หรือไม่หากข้อบกพร่องทำให้เกิดการใช้งานจริง? ใช่ ในการซื้อคืนนี้ เราจะเปลี่ยนกลับโดยการรวม PR ใน Github ไปป์ไลน์การปรับใช้จะใช้เวลา 4-5 นาที อาจใช้เวลานานกว่าในการอนุมัติ PR ของคุณ
  • เราสามารถตรวจพบปัญหาการผลิตได้อย่างรวดเร็วหรือไม่? ไม่ ไม่มีการแจ้งเตือนใด ๆ ใน repo นี้ ดังนั้นหากมันล่ม เราก็คงไม่ทราบทันที AWS จัดเตรียมแดชบอร์ดเริ่มต้นบางส่วนสำหรับเกตเวย์ API และ Lambda
  • เราสามารถเปลี่ยนไปใช้ PyTorch โดยไม่ต้องหยุดทำงานได้หรือไม่? ใช่ repo นี้ถูกนำไปใช้เพื่อให้เราสามารถสร้างการเปลี่ยนแปลงแมชชีนเลิร์นนิงที่สำคัญ เช่น การสลับเฟรมเวิร์ก ทดสอบ และปรับใช้โดยไม่ต้องหยุดทำงานและมีความสามารถในการเปลี่ยนกลับได้ ตราบใดที่การเปลี่ยนแปลงเฟรมเวิร์กเป็นการ PR เดียว

มีวิธีใดบ้างที่งานที่ยังไม่ผ่านการทดสอบอาจถูกนำไปใช้งาน

การทดสอบของฉันมีช่องว่างหลายประการ:

  • ส่วนประกอบระหว่างคำขอ API และ Lambda ยังไม่ได้รับการทดสอบ: แพคเกจ Docker, การโต้ตอบระหว่างโค้ด Lambda และเกตเวย์ API และการกำหนดค่าเกตเวย์ API สามารถทดสอบส่วนของ Docker ได้อย่างง่ายดายโดยการรันการทดสอบหน่วยภายใน Docker แม้ว่าการทดสอบจะทำให้การทดสอบช้าลงเล็กน้อยก็ตาม สามารถทดสอบการกำหนดค่า AWS ได้ดีขึ้นโดยใช้ AWS SAM เพื่อหมุนสแต็กเวอร์ชันในเครื่องสำหรับการทดสอบ ปัญหานี้ยังส่งผลต่อประสิทธิภาพการทำงานของนักพัฒนาด้วย เช่น หากนักพัฒนากำลังเปลี่ยนวิธีการจัดแพ็คเกจการขึ้นต่อกัน พวกเขาอาจจำเป็นต้องแก้ไข Dockerfile และจะดีที่สุดหากพวกเขาสามารถทดสอบบนคอมพิวเตอร์ของตนได้
  • ช่องว่างอีกประการหนึ่งขึ้นอยู่กับเวลา: การขึ้นต่อกันสำหรับการทดสอบหน่วยจะถูกติดตั้งในเวลาที่แตกต่างจากการขึ้นต่อกันสำหรับอิมเมจ Docker เนื่องจากการขึ้นต่อกันไม่ได้รับการปักหมุดอย่างสมบูรณ์ (ดูโพสต์ก่อนหน้าว่าทำไม) นั่นหมายความว่าในทางทฤษฎีแล้ว ไลบรารีเวอร์ชันใหม่สามารถนำไปใช้งานระหว่างการทดสอบและการปรับใช้ซึ่งอาจทำให้เกิดการหยุดทำงานของการผลิต วิธีหนึ่งในการแก้ไขปัญหานี้คือการสร้างอิมเมจ Docker ในไปป์ไลน์ PR ใช้สำหรับการทดสอบหน่วย จากนั้นอัปโหลดไปยัง ECR ซึ่งจะช่วยประหยัดเวลาในการใช้งานอีกด้วย อีกทางเลือกหนึ่งคือการปักหมุดการขึ้นต่อกันให้มากขึ้น
  • ตามทฤษฎีแล้ว บางคนสามารถเขียนทับไฟล์โมเดลใน S3 และเลี่ยงการทดสอบอัตโนมัติของเราได้ แม้ว่าการดำเนินการนี้จะน่ารำคาญก็ตาม หากพวกเขาทำเช่นนั้น มันจะไม่กระตุ้นการปรับใช้จริง ๆ เพราะว่ามันจะไม่มีการเปลี่ยนแปลงใน Github จะต้องเป็นเส้นทางไฟล์เดียวกันทุกประการบน S3

นักพัฒนาใหม่เรียนรู้ได้ง่ายหรือไม่

เป็นการยากที่จะตัดสินงานของคุณเองเพื่อการเรียนรู้ เมื่อใดก็ตามที่เป็นไปได้ จะเป็นการดีกว่าถ้ามีคนมาตรวจสอบงานและอธิบายความท้าทายที่พวกเขาเผชิญ

  • ในความคิดของฉัน นี่เป็นเรื่องยากสำหรับผู้เริ่มต้นที่จะเรียนรู้เนื่องจากมีเครื่องมือมากมาย ความซับซ้อนส่วนใหญ่เกิดขึ้นในระบบอัตโนมัติ ซึ่งนักพัฒนาอาจไม่จำเป็นต้องเข้าใจอย่างถ่องแท้ แต่ DVC นำเสนอแนวคิดใหม่ แทนที่จะทำแค่ git clone พวกเขายังต้องดึงไฟล์ DVC เพื่อให้ repo ทำงานได้ ที่สามารถปรับปรุงได้ด้วย git hooks
  • ความท้าทายที่เกี่ยวข้องกับ DVC คือต้องใช้ข้อมูลรับรอง AWS ตอนนี้นักพัฒนาซอฟต์แวร์ของคุณต้องมีการตั้งค่า AWS เพื่อทำงานใน Repo และคุณต้องตรวจสอบให้แน่ใจว่าพวกเขามีสิทธิ์ที่เหมาะสมในบัคเก็ต S3
  • สำหรับนักพัฒนาที่มีประสบการณ์ AWS มาก่อน การเรียนรู้นี้อาจง่ายกว่า repo ที่มีเครื่องมือหรือแพลตฟอร์มเฉพาะของ ML มากมาย
  • Terraform เพิ่มภาษาการเขียนโปรแกรมและวิธีคิดอีกแบบหนึ่ง หากฉันทำได้อีกครั้ง ฉันจะลองแทนที่ส่วนนั้นด้วย CDK เพื่อที่นักพัฒนาจะได้ไม่ต้องเรียนรู้ภาษาโครงสร้างพื้นฐานอื่น
  • API เกตเวย์และ Lambda อาจเป็นเรื่องยากที่จะเข้าใจ และไม่ใช่เทคโนโลยีที่ยอดเยี่ยมสำหรับนักพัฒนารุ่นเยาว์ตามประสบการณ์ของฉัน
  • การใช้ “dvc repro” อาจไม่คุ้มค่ากับความพยายามในการเรียนรู้
  • ในความคิดของฉัน มันยังไม่ได้รับการบันทึกไว้เพียงพอสำหรับนักพัฒนารุ่นน้อง
  • ในความคิดของฉัน พารามิเตอร์ของตัวจัดการ Lambda นั้นเรียนรู้ได้ยาก เนื่องจากไม่มีการเติมข้อความอัตโนมัติสำหรับโครงสร้างข้อมูล Lambda Powertools สามารถช่วยได้ แต่เพิ่มการพึ่งพาอื่น

นักพัฒนาจะต้องเผชิญกับความลำบากอย่างมากในขณะที่ใช้ repo นี้หรือไม่

แม้ว่าส่วนย่อยก่อนหน้านี้จะเกี่ยวกับนักพัฒนารายใหม่ แต่นี่เป็นเรื่องเกี่ยวกับประสบการณ์ในแต่ละวันของนักพัฒนาที่มีประสบการณ์

  • มีสถานการณ์ที่อาจต้องใช้ PR หลายรายการใน repos หรือไม่ การฝึกอบรม การให้บริการ และโครงสร้างพื้นฐานทั้งหมดอยู่ใน repo เดียวกัน ดังนั้นการเปลี่ยนแปลงที่สำคัญจึงสามารถทำได้ในการประชาสัมพันธ์เดียวที่ผ่านการทดสอบอย่างดี ในทางกลับกัน อาจเป็นไปได้ว่ารหัสนี้กำลังถูกเรียกโดย repo อื่นที่ไม่แสดง และการเปลี่ยนแปลงที่เกี่ยวข้องกับรูปร่างอินพุต JSON อาจต้องใช้ PR ใน repos
  • repo นี้จะทำงานได้ดีกับเครื่องสแกนความปลอดภัยเช่น depandabot หรือไม่ ในอดีต ฉันประสบปัญหาในการอัปเดตแพ็กเกจ ML ที่ dependabot ติดธงไว้ได้อย่างง่ายดาย ด้วยการตั้งค่า repo นี้ คุณสามารถกำหนดค่า dependabot เพื่อเปิด PR ที่มีการขึ้นต่อกันใหม่ สร้างโมเดลที่อัปเดต และทดสอบว่าโมเดลเหล่านั้นทำงานในบริการได้หรือไม่ หาก PR ผ่าน เราก็สามารถรวมมันเข้าด้วยกันเพื่ออัปเดตการขึ้นต่อกัน
  • แม้ว่าการทำให้ใช้งานได้จะใช้เวลา 4-5 นาที แต่จะดีกว่าหากใช้เวลาประมาณ 1 นาที โชคดีที่ Github Actions ให้เวลาเราในการติดตามตามขั้นตอน เพื่อให้เราสามารถตรวจสอบเพิ่มเติมได้ ตัวอย่างเช่น การติดตั้ง DVC และ CDK ทุกครั้งจะใช้เวลาเกิน 1 นาทีเล็กน้อย การรันไปป์ไลน์ CDK รวมถึงการสร้าง Docker ใช้เวลาประมาณ 3 นาที

ข้อกังวลด้านความปลอดภัยใดๆ

ฉันไม่ใช่ผู้เชี่ยวชาญด้านความปลอดภัย แต่ฉันได้มีส่วนร่วมในการตรวจสอบเพียงพอที่จะตรวจสอบพื้นฐาน

  • ความปลอดภัยของ IAM สำหรับบทบาท Lambda หรือไม่ CDK จะสร้างบทบาท IAM ขั้นต่ำให้เราโดยอัตโนมัติ แม้ว่าผู้โจมตีจะพบวิธีเรียกใช้โค้ดที่กำหนดเองภายใน Lambda ของเรา แต่บทบาทดังกล่าวก็ไม่ได้รับอนุญาตให้ทำอะไรได้มากนัก
  • ความปลอดภัยของ IAM สำหรับผู้ใช้ Github Actions หรือไม่ ผู้ใช้รายนี้มีสิทธิ์พอสมควรและผู้โจมตีอาจทำอันตรายได้หากพวกเขามีสิทธิ์เข้าถึง ความลับของ Github Actions นั้นค่อนข้างปลอดภัย ปัญหาทั่วไปที่ใหญ่ที่สุดคือฉันจะต้องหมุนเวียนข้อมูลรับรอง IAM ด้วยตนเองหากข้อมูลรั่วไหล
  • หากคุณกำลังปรับใช้สิ่งนี้เพื่อทำงานกับข้อมูลการฝึกอบรมที่ละเอียดอ่อน Github Actions อาจไม่ใช่ตัวเลือก
  • API เปิดกว้างสู่โลกภายนอกอย่างสมบูรณ์ หากโมเดลมีเอกลักษณ์เฉพาะตัวและมีคุณค่าสูง คุณไม่ต้องการให้ผู้อื่นตรวจสอบโมเดลนั้นอย่างไม่เลือกหน้า การกรองการรับรองความถูกต้องและ/หรือการกรอง IP สามารถช่วยได้
  • ไฟล์ดองอนุญาตให้มีการเรียกใช้โค้ดโดยพลการ แต่ในการเขียนไฟล์ดองเหล่านั้น จะต้องมีคนมีสิทธิ์เขียนไปยังบัคเก็ต S3 ของคุณและจะต้องทริกเกอร์การปรับใช้ใหม่จาก Github ไฟล์ Pickle ถือเป็นเวกเตอร์การโจมตีที่ไม่น่าเป็นไปได้ในระบบนี้
  • หากมีช่องโหว่ในอิมเมจ Docker จะไม่ได้รับการอัปเดตจนกว่าจะมีการปรับใช้ครั้งถัดไป นี่เป็นข้อเสียอย่างหนึ่งของการใช้ Docker สำหรับแลมบ์ดา เราคือคนที่จัดการการพึ่งพาระบบเหล่านี้ แทนที่จะจัดการ AWS เหมือนที่ทำกับแลมบ์ดาไฟล์ zip

ความกังวลในบริษัทที่เกิดขึ้นจริง

หากคุณกำลังสร้างระบบอย่างมืออาชีพ repo นี้ไม่เพียงพอสำหรับเทมเพลต นี่คือรายการข้อควรพิจารณาที่ฉันมีในอุตสาหกรรม:

  • การรักษาความปลอดภัยปลายทาง (การรับรองความถูกต้อง การจัดการความลับ ข้อจำกัด IP ฯลฯ)
  • เสถียรภาพปลายทาง (DNS)
  • การกำหนดเวอร์ชันตำแหน่งข้อมูล — หากโครงสร้างข้อมูลอินพุตหรือเอาต์พุตมีการเปลี่ยนแปลงในลักษณะที่ไม่สามารถเข้ากันได้แบบย้อนหลัง คุณอาจต้องรองรับโครงสร้างข้อมูลหลายรายการและมีเวอร์ชันของตำแหน่งข้อมูลเอง
  • CORS เมื่อมันถูกรวมเข้ากับส่วนหน้าที่ให้บริการจากโดเมนอื่น และการเพิ่มประสิทธิภาพเวลาแฝงที่เกี่ยวข้องกับ CORS
  • PyPI ส่วนตัวภายใน Docker หากคุณใช้โมดูลภายในใด ๆ
  • ขั้นตอนการปรับใช้หลายขั้นตอน: dev, staging, prod
  • การทดสอบการบูรณาการ การทดสอบแบบ end-to-end และการทดสอบหน่วยสำหรับตรรกะทางธุรกิจใดๆ
  • การทำงานพร้อมกันที่มีการเตรียมใช้งาน ตั้งค่าเพื่ออุ่นเครื่อง Lambda ก่อน เกตเวย์ API จะเปลี่ยนไปใช้เวอร์ชันใหม่ เพื่อให้เรามีเวลาหยุดทำงานเป็นศูนย์
  • แดชบอร์ดและการเตือน
  • การฝึกอบรมการกำหนดเวอร์ชันข้อมูล การอัปเดตข้อมูล และการทดสอบข้อมูล — หากคุณได้รับข้อมูลใหม่เมื่อเวลาผ่านไป ก็จำเป็นต้องมีไปป์ไลน์ทั้งหมดก่อนการฝึก

ตอนนี้ฉันกำลังคิดถึงธุรกิจนี้ ก็เป็นเวลาที่ดีที่จะพูดถึงภาพรวม เมื่อคุณเขียน API มันเป็นส่วนหนึ่งของระบบที่ใหญ่กว่า ฉันได้เห็นความเสียหายอย่างมากที่เกิดจากการมุ่งเน้นไปที่ repo เพียงอันเดียวมากเกินไปและโค้ดที่เรียก API น้อยเกินไป ตัวอย่างเช่น เราสามารถเปลี่ยน API ของเราโดยไม่ต้องบอกใคร และอาจทำให้ผลิตภัณฑ์ของเราล่มได้

ในทำนองเดียวกัน การพิจารณาประสบการณ์ของนักพัฒนาที่ใช้ API ของเราก็เป็นเรื่องดี เช่น เรียนรู้และใช้งานได้ง่ายหรือไม่ คนอื่นๆ ได้กล่าวถึงหัวข้อประสบการณ์ของนักพัฒนาสำหรับ API ดีกว่าที่ฉันสามารถทำได้:

หากคุณกำลังปรับใช้บริการในบัญชี AWS เดียวกันกับบริการอื่นๆ มีข้อควรพิจารณาเพิ่มเติมบางประการ:

  • บริการนี้ตั้งชื่อและติดแท็กเพื่อให้คุณสามารถตรวจสอบใบเรียกเก็บเงิน AWS ของคุณได้อย่างง่ายดายหรือไม่ ฉันขอแนะนำอย่างน้อยให้แท็กทรัพยากรทั้งหมดกับทีมที่ผลิตทรัพยากรเหล่านั้น
  • หากคุณไม่มีขีดจำกัดการทำงานพร้อมกันของ Lambda โปรดทราบว่า Lambda ทั้งหมดในบัญชีและภูมิภาคเดียวกันมีขีดจำกัดการทำงานพร้อมกันร่วมกัน ดังนั้นหาก Lambda ของคุณใช้การทำงานพร้อมกัน 999 รายการและขีดจำกัดคือ 1,000 รายการ Lambda อื่นๆ ในบริษัทจะทำงานได้ครั้งละ 1 รายการเท่านั้น และรายการอื่นๆ จะถูกควบคุมปริมาณ (ฉันพูดแบบนี้จากประสบการณ์จริงในการผลิต Lambdas ที่ถูกควบคุมปริมาณเนื่องจากบริการบันทึกข้อมูลทำลายการทำงานพร้อมกันของเรา)

ลำดับความสำคัญในการแก้ไข

ฉันกล่าวถึงปัญหามากมายกับ repo ตัวอย่างนี้ ในอุตสาหกรรม เรามักจะไม่สามารถแก้ไขทุกสิ่งที่เราต้องการได้ อย่างน้อยก็ไม่ใช่ในทันที เราจึงต้องจัดลำดับความสำคัญว่าควรปรับปรุงอะไรก่อน

การจัดลำดับความสำคัญควรได้รับคำแนะนำจากความต้องการทางธุรกิจ ความต้องการของผู้ใช้ และวิธีที่บริการของคุณรวมอยู่ในผลิตภัณฑ์ หากนี่เป็นบริการที่สำคัญต่อภารกิจที่เกี่ยวข้องกับข้อมูลส่วนตัว นั่นจะแตกต่างอย่างมากจากบริการดีๆ ที่ไม่สามารถทำลายผลิตภัณฑ์ได้ หากเป็น API ที่นักพัฒนาภายนอกใช้ซึ่งแตกต่างจาก API ภายในอย่างมาก หากเป็นบริการที่รองรับคำขอ 100,000 คำขอต่อชั่วโมง ซึ่งต่างจาก 100 คำขอต่อชั่วโมง และอื่นๆ

ต่อไปนี้คือตัวอย่างการจัดลำดับความสำคัญที่อาจเหมาะสมสำหรับการเริ่มต้นระบบขนาดเล็ก:

ลำดับความสำคัญสูง

  • ชื่อโดเมน เพื่อที่เราจะได้ไม่ทำให้เกิดการหยุดทำงานโดยไม่ได้ตั้งใจเมื่อเราเปลี่ยนเกตเวย์ API
  • จัดเตรียมการทำงานพร้อมกันเพื่อให้มีเวลาหยุดทำงานน้อยลงเมื่อปรับใช้การอัปเดต
  • พูดคุยกับผู้ที่รวม API หากไม่ใช่คุณ!
  • การปฏิบัติตามข้อกำหนดที่จำเป็น

ลำดับความสำคัญปานกลาง

  • การกรอง IP การตรวจสอบสิทธิ์ และ/หรือความปลอดภัยที่คุณเลือก — ซึ่งอาจมีค่าตั้งแต่สูงไปต่ำ ขึ้นอยู่กับแอปพลิเคชัน
  • สัญญาณเตือนไฟฟ้าดับ — อาจมีระดับสูงไปต่ำ ขึ้นอยู่กับการใช้งาน
  • การใช้งานแบบไม่ต้องหยุดทำงานอย่างแท้จริง

ลำดับความสำคัญต่ำ

  • อย่างอื่น

ทางเลือก

ฉันยังได้ลองทางเลือกอื่นด้วย และฉันจะพูดถึงสิ่งที่ฉันพบโดยย่อ

ปรับใช้กับ AWS ECS

AWS Elastic Container Service (ECS) เป็นตัวเลือกที่ดีกว่า Lambda สำหรับคนส่วนใหญ่ โดยจะเปิดอยู่เสมอ ดังนั้นคุณจึงไม่ต้องกังวลกับเวลาที่ใช้ในการโหลดโมเดลของคุณเมื่อทำการบูท ข้อเสียของ ECS คือการตั้งค่าการปรับขนาดอัตโนมัติให้ถูกต้องเป็นเรื่องยากและอาจมีราคาแพงกว่า

ปรับใช้กับ Cortex.dev

ฉันพบว่า Cortex.dev นั้นรวดเร็วและง่ายดายอย่างน่าพึงพอใจ การปรับใช้นั้นเร็วกว่าสิ่งอื่นใดมาก และไม่ซับซ้อนไปกว่า Heroku มากนัก ฉันจำได้ว่าตอนนั้นคิดว่าทีมรักษาความปลอดภัยของฉันอาจไม่ชอบบางแง่มุมของการปรับใช้

การกำหนดเวอร์ชันโมเดลด้วย git-lfs บน Github

ฉันเริ่มต้นด้วยการใช้ git-lfs สำหรับการกำหนดเวอร์ชันโมเดล และ Heroku free tier สำหรับการปรับใช้ ฉันอยากใช้ Github เป็นผู้ให้บริการ git-lfs หลังจากที่ประสบปัญหากับทางเลือกอื่นในที่ทำงาน

ฉันได้เรียนรู้แล้วว่าทำไมเราไม่ใช้ git-lfs ของ Github! เมื่อคุณถึงโควต้า LFS repo ของคุณจะถูกล็อคจนกว่าคุณจะซื้อโควต้าเพิ่มหรือลบ repo ครั้งล่าสุดที่ฉันตรวจสอบ Github ไม่มีวิธีเพิ่มโควต้าของคุณโดยอัตโนมัติ ดังนั้นคุณจึงต้องพึ่งพาผู้ที่อัปเกรดเว็บไซต์ด้วยตนเองเป็นระยะๆ

สิ่งที่ฉันไปไม่ถึง

ทุกวันนี้มีเครื่องมือ MLOps มากมาย! สำหรับแต่ละไลบรารี เครื่องมือ หรือแพลตฟอร์มที่ฉันใช้ มีทางเลือกมากมาย ฉันได้ยินเรื่องดีๆ เกี่ยวกับ kubeflow มาก่อน และอาจเป็นเวลาที่ดีที่จะประเมิน Sagemaker อีกครั้ง

ฉันยังเห็นบางคนปรับใช้โมเดลด้วยโค้ดโดยใช้แพ็คเกจ Python นั่นดูเหมือนเป็นวิธีที่ดีในการรวมโค้ด ข้อมูล และการขึ้นต่อกันเข้าด้วยกัน

ข้อสรุป

ฉันหวังว่าฉันจะสามารถอธิบายแนวคิดทั่วไปในบริการเว็บการเรียนรู้ของเครื่องได้!

แม้ว่าฉันจะใช้ AWS Lambda ในตัวอย่างนี้ แต่ก็มีสาเหตุหลักมาจากความอยากรู้อยากเห็น โปรดอย่าตีความโพสต์นี้เป็นการรับรอง Lambda สำหรับบริการ Machine Learning Lambda จะเป็นตัวเลือกที่ดีหรือไม่นั้นขึ้นอยู่กับความต้องการของคุณ

โปรดทราบว่านี่อาจเป็นแนวทางสำหรับการเริ่มต้นธุรกิจขนาดเล็ก แต่ฉันสงสัยว่ามันพร้อมสำหรับการดำเนินงานในขนาดที่ใหญ่ขึ้นหรือไม่ และแน่นอนว่าฉันละทิ้งงาน MLOps ทั่วไปบางส่วนออกไป เช่น การสนับสนุนสำหรับการทดลอง

ขอบคุณที่อ่าน! หากมีบางส่วนของบทความที่คุณพบว่าทำให้เกิดความสับสน โปรดแจ้งให้เราทราบ