המרת מודלים של ML ל-Core ML עבור iOS

המרה ישירה של מודל ML ל-Core ML גורמת לעיתים קרובות לשגיאות ואובדן דיוק. אנו מפתחים ומבצעים אופטימיזציה של קבצי .mlpackage עבור iOS, תוך התגברות על מגבלות הכלים ושמירה על איכות החיזוי. הצוות שלנו מספק את הפרויקט במפתח מלא—מביקורת המודל המקורי ועד לפריסה עם תמיכה מתמשכת.

פיתוח ותמיכה בכל סוגי היישומים הניידים:

יישומים ניידים למידע ובידור
אפליקציות חדשות, משחקים, מדריכים, קטלוגים מקוונים, אפליקציות מזג אוויר, אפליקציות כושר ובריאות, אפליקציות נסיעות, אפליקציות חינוכיות, רשתות חברתיות ומסנג'רים, חידונים, בלוגים ופודקאסטים, פורומים, אגרגטורים
יישומים ניידים למסחר אלקטרוני
חנויות מקוונות, אפליקציות B2B, מרקטפלייסים, בורסות מקוונות, שירותי קאשבק, בורסות, פלטפורמות דרופשיפינג, תוכניות נאמנות, משלוחי מזון ומוצרים, מערכות תשלום.
יישומים ניידים לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, ניהול פרויקטים, כלים לצוותי מכירות, ניהול פיננסי, ניהול ייצור, ניהול לוגיסטיקה ומשלוחים, ניהול משאבי אנוש, מערכות ניטור נתונים
יישומים ניידים לשירותים אלקטרוניים
פלטפורמות מודעות מסווגות, בתי ספר מקוונים, בתי קולנוע מקוונים, פלטפורמות שירות אלקטרוניות, פלטפורמות קאשבק, אירוח וידאו, פורטלים נושאיים, פלטפורמות הזמנות ותזמון מקוונות, פלטפורמות מסחר מקוונות

אלה הם רק חלק מסוגי היישומים הניידים שאנו עובדים איתם, ולכל אחד מהם עשויים להיות מאפיינים ופונקציונליות ספציפיים משלו, המותאמים לצרכים ולמטרות הספציפיות של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 1734 השירותים
המרת מודלים של ML ל-Core ML עבור iOS
בינוני
מ- 1 יום עד 3 ימים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית מובייל עבור FEEDME
    פיתוח אפליקציית מובייל עבור FEEDME
    971
  • פיתוח אפליקציית מובייל עבור XOOMER
    פיתוח אפליקציית מובייל עבור XOOMER
    869
  • פיתוח אפליקציית מובייל עבור RHL
    פיתוח אפליקציית מובייל עבור RHL
    1289
  • פיתוח אפליקציית מובייל עבור ZIPPY
    פיתוח אפליקציית מובייל עבור ZIPPY
    1143
  • פיתוח אפליקציית מובייל עבור Affhome
    פיתוח אפליקציית מובייל עבור Affhome
    1076
  • פיתוח אפליקציית מובייל עבור חברת FLAVORS
    פיתוח אפליקציית מובייל עבור חברת FLAVORS
    677

המרת מודלי ML ל-Core ML עבור iOS

בעיות בהמרה ל-Core ML

קבלת קובץ .mlpackage מ-PyTorch או TensorFlow כוללת שלבים ספציפיים, וכל אחד מהם עלול להיכשל מסיבות לא ברורות. הכלי coremltools (כלי ההמרה הרשמי) של Apple אינו תומך בכל הפעולות — לדוגמה, einsum, קונבולוציות עם גדלים דינמיים, או ענפים בגרף. המרה ישירה גורמת לעיתים קרובות לשגיאות או לירידה של 5–10% בדיוק. צברנו ניסיון בפתרון מקרים כאלה: במשך למעלה מ-5 שנות עבודה, המרנו יותר מ-50 מודלים למגזר הפיננסי, לקמעונאות וליישומי AR. בממוצע, ההסקה באייפון מואצת ב-30–50% לאחר אופטימיזציה ל-Apple Neural Engine — וזה טוב משמעותית מכלי ההמרה הסטנדרטיים. החל מ-$200, ההשקעה מחזירה את עצמה לעיתים קרובות תוך שבועות בזכות שיפור חוויית המשתמש והפחתת עלויות השרת. לדוגמה, לקוח קמעונאי אחד הפחית את עלויות השרת ב-$500 לחודש לאחר המרת מודל ההמלצות שלהם. עלות ההמרה נעה בין $200 ל-$1000 בהתאם למורכבות המודל. הזמינו המרה וקבלו קובץ .mlpackage מוכן עם אימות דיוק.

מדוע המרה ל-Core ML דורשת מומחיות

כלי ההמרה אינו תומך בכל פעולות PyTorch/TensorFlow, ואחרות דורשות הגדרת פרמטרים נכונה. המרה ישירה מובילה לעיתים קרובות לשגיאות או לפגיעה בדיוק. אנו פותרים בעיות אלה על ידי בחירת פרמטרים אופטימליים ושימוש בפעולות מותאמות אישית בעת הצורך.

הכנת המודל להמרה

לפני ההמרה, המודל חייב להיות במצב eval עם משקלים קפואים. torch.jit.trace דורש קלט לדוגמה — הוא מתעד את הגרף עבור צורה ספציפית:

import torch
import torchvision
import coremltools as ct

model = MyModel()
model.load_state_dict(torch.load("weights.pth", map_location="cpu"))
model.eval()

# trace — фиксирует граф для конкретного shape
example_input = torch.zeros(1, 3, 224, 224)
traced_model = torch.jit.trace(model, example_input)
# Для ветвлений (if/else) используйте torch.jit.script:
# scripted_model = torch.jit.script(model)

# Конвертация в mlprogram
mlmodel = ct.convert(
    traced_model,
    inputs=[ct.ImageType(
        name="input",
        shape=ct.Shape(shape=(1, 3, 224, 224)),
        color_layout=ct.colorlayout.RGB,
        bias=[-0.485/0.229, -0.456/0.224, -0.406/0.225],
        scale=1/(255.0 * 0.229)
    )],
    outputs=[ct.TensorType(name="logits")],
    compute_precision=ct.precision.FLOAT16,
    minimum_deployment_target=ct.target.iOS16,
    convert_to="mlprogram"
)

mlmodel.short_description = "Image classifier"
mlmodel.input_description["input"] = "RGB image 224x224"
mlmodel.output_description["logits"] = "Class probabilities"
mlmodel.save("MyModel.mlpackage")

אם ההמרה הישירה נכשלת, השתמשו ב-ONNX כשלב ביניים: import torch import torchvision import coremltools as ct model = MyModel() model.load_state_dict(torch.load("weights.pth", map_location="cpu")) model.eval() # trace — фиксирует граф для конкретного shape example_input = torch.zeros(1, 3, 224, 224) traced_model = torch.jit.trace(model, example_input) # Для ветвлений (if/else) используйте torch.jit.script: # scripted_model = torch.jit.script(model) # Конвертация в mlprogram mlmodel = ct.convert( traced_model, inputs=[ct.ImageType( name="input", shape=ct.Shape(shape=(1, 3, 224, 224)), color_layout=ct.colorlayout.RGB, bias=[-0.485/0.229, -0.456/0.224, -0.406/0.225], scale=1/(255.0 * 0.229) )], outputs=[ct.TensorType(name="logits")], compute_precision=ct.precision.FLOAT16, minimum_deployment_target=ct.target.iOS16, convert_to="mlprogram" ) mlmodel.short_description = "Image classifier" mlmodel.input_description["input"] = "RGB image 224x224" mlmodel.output_description["logits"] = "Class probabilities" mlmodel.save("MyModel.mlpackage") , ולאחר מכן import numpy as np import PIL.Image img = PIL.Image.open("test.jpg").resize((224, 224)) transform = torchvision.transforms.Compose([ torchvision.transforms.ToTensor(), torchvision.transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) tensor = transform(img).unsqueeze(0) with torch.no_grad(): pytorch_out = model(tensor).numpy() coreml_out = mlmodel.predict({"input": img})["logits"] max_diff = np.max(np.abs(pytorch_out - coreml_out)) print(f"Max difference: {max_diff}") .

אימות נכונות ההמרה

כדי להבטיח נאמנות, השוו את הפלטים של המודל המקורי ושל Core ML על תמונת בדיקה:

import numpy as np
import PIL.Image

img = PIL.Image.open("test.jpg").resize((224, 224))
transform = torchvision.transforms.Compose([
    torchvision.transforms.ToTensor(),
    torchvision.transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
tensor = transform(img).unsqueeze(0)

with torch.no_grad():
    pytorch_out = model(tensor).numpy()

coreml_out = mlmodel.predict({"input": img})["logits"]
max_diff = np.max(np.abs(pytorch_out - coreml_out))
print(f"Max difference: {max_diff}")

ערך תקין עבור FP16 הוא פחות מ-0.01. אם ההפרש > 0.05, בדקו את הנורמליזציה ב-ct.ImageType.

הגדרת גדלי קלט משתנים

ניתן להשתמש ב-# Диапазон размеров flexible_shape = ct.Shape( shape=(1, 3, ct.RangeDim(min_val=64, max_val=1024), ct.RangeDim(min_val=64, max_val=1024)) ) # Набор конкретных размеров enumerated_shapes = ct.EnumeratedShapes( shapes=[ ct.Shape(shape=(1, 3, 224, 224)), ct.Shape(shape=(1, 3, 384, 384)), ct.Shape(shape=(1, 3, 512, 512)), ] ) mlmodel = ct.convert(traced_model, inputs=[ct.TensorType(name="input", shape=enumerated_shapes)]) עם @ct.converters.mil.register_torch_op() def my_custom_op(context, node): x = context[node.inputs[0]] result = mb.custom(params={"...": "..."}, inputs={"x": x}, ...) context.add(result) או import CoreML @objc(MyCustomLayer) class MyCustomLayer: NSObject, MLCustomLayer { required init(parameters: [String: Any]) throws { } func setWeightData(_ weights: [Data]) throws { } func outputShapes(forInputShapes inputShapes: [[NSNumber]]) throws -> [[NSNumber]] { ... } func evaluate(inputs: [MLMultiArray], outputs: [MLMultiArray]) throws { } } עבור ממדי קלט גמישים. לדוגמה:

# Диапазон размеров
flexible_shape = ct.Shape(
    shape=(
        1,
        3,
        ct.RangeDim(min_val=64, max_val=1024),
        ct.RangeDim(min_val=64, max_val=1024),
    )
)

# Набор конкретных размеров
enumerated_shapes = ct.EnumeratedShapes(
    shapes=[
        ct.Shape(shape=(1, 3, 224, 224)),
        ct.Shape(shape=(1, 3, 384, 384)),
        ct.Shape(shape=(1, 3, 512, 512)),
    ]
)

mlmodel = ct.convert(
    traced_model,
    inputs=[ct.TensorType(name="input", shape=enumerated_shapes)],
)

פעולות מותאמות אישית

אם המודל מכיל פעולה ש-coremltools אינו מזהה, הוסיפו שכבה מותאמת אישית ב-Swift. ב-Python, רשמו את הפעולה באמצעות PYTORCH_OPS_REGISTRY. דוגמה למימוש ב-Swift:

@ct.converters.mil.register_torch_op()
def my_custom_op(context, node):
    x = context[node.inputs[0]]
    result = mb.custom(params={"...": "..."}, inputs={"x": x}, ...)
    context.add(result)

שכבה מותאמת אישית רצה על CPU — לביצועים, העדיפו פעולות סטנדרטיות. בפרויקט אחד, שכבת attention מותאמת אישית דרשה עטיפת המודל כדי להחליף את einops בפעולות סטנדרטיות — פתרנו זאת על ידי tracing עם אורך רצף קבוע.

השוואת ביצועים במכשירים

ה-Apple Neural Engine מאיץ את ההסקה פי 10–20 בהשוואה ל-CPU. תוצאות עבור ResNet-50 (224x224):

מכשיר CPU (ms) GPU (ms) ANE (ms)
iPhone 14 Pro 45 30 12
iPhone 13 60 40 18
iPhone SE (דור 3) 90 65 30

השוואת פורמטי Core ML

פרטי פורמט (mlprogram לעומת neuralnetwork)
פרמטר mlprogram neuralnetwork
גרסת iOS 15+ 12+
תמיכה ב-ANE כן לא
FP16 כן FP32 בלבד
גודל קטן יותר גדול יותר
ביצועים גבוהים יותר נמוכים יותר

מה כלול בעבודת ההמרה?

  • ביקורת מודל — ניתוח גרף, זיהוי פעולות לא תואמות, המלצות ל-refactoring. בדיקת תמיכה ב-ANE.
  • המרה — בחירת דיוק (FP16/INT8), גודל קלט, פתרון שגיאות. פעולות מותאמות אישית במידת הצורך.
  • אימות דיוק — השוואה על 100+ דגימות בדיקה, דוח עם הפרש מקסימלי ומדדים (accuracy, mAP).
  • אופטימיזציה ל-ANE — החלפת Reshape/Permute בפעולות תואמות ANE, הסרת צווארי בקבוק, קוונטיזציה.
  • תיעוד — תיאור פרמטרי המודל, אינטגרציה ב-Xcode, דוגמת שימוש.

הלקוחות שלנו רואים בדרך כלל הפחתת עלויות שרת של $500–$2000 לחודש. מחיר ההמרה הממוצע נע בין $200 ל-$1000 בהתאם למורכבות.

תהליך ההמרה: שלב אחר שלב

  1. ייצאו את המודל ל-TorchScript באמצעות # Квантование в INT8 через coremltools mlmodel_int8 = ct.compression_utils.affine_quantize_weights(mlmodel, mode="linear_symmetric") mlmodel_int8.save("MyModel_INT8.mlpackage") או torch.jit.script. ודאו שהמודל במצב eval עם משקלים קפואים.
  2. הריצו המרה עם סוגי קלט/פלט מתאימים, דיוק (FP16 מומלץ) ויעד פריסה.
  3. בצעו אימות נומרי על ידי השוואת פלטים על קבוצת בדיקה מייצגת. ודאו שההפרש המקסימלי < 0.01.
  4. אם מתרחשות שגיאות, זהו פעולות לא נתמכות והחליפו אותן או השתמשו ב-ONNX כשלב ביניים.
  5. לאחר המרה מוצלחת, בצעו קוונטיזציה ל-INT8 אם נדרשת האצה נוספת, ולאחר מכן בדקו על מכשירי היעד.

קוונטיזציה: INT8 לעומת FP16

בנוסף להמרה ל-mlprogram, קוונטיזציה מפחיתה עוד יותר את גודל המודל ומאיצה את ההסקה. FP16 מקטין את נפח המשקלים בחצי עם אובדן דיוק זניח — זהו התקן המוגדר כברירת מחדל שלנו. INT8 מקטין את המודל בעוד פי 2 אך דורש מערך כיול להערכת שגיאת הקוונטיזציה. ב-Apple Neural Engine, INT8 מהיר במיוחד: רווח המהירות הוא פי 1.5–2 בהשוואה ל-FP16. עבור משימות סיווג וזיהוי עצמים, אובדן הדיוק של INT8 בדרך כלל אינו עולה על 1–2%. עבור משימות טקסט או יצירה, FP16 עדיף.

import CoreML

@objc(MyCustomLayer)
class MyCustomLayer: NSObject, MLCustomLayer {
    required init(parameters: [String: Any]) throws {
    }

    func setWeightData(_ weights: [Data]) throws {
    }

    func outputShapes(forInputShapes inputShapes: [[NSNumber]]) throws -> [[NSNumber]] {
        ...
    }

    func evaluate(inputs: [MLMultiArray], outputs: [MLMultiArray]) throws {
    }
}

הצוות שלנו מבצע בדיקות השוואתיות של שני הפורמטים על המכשיר הספציפי שלכם ובוחר את האפשרות האופטימלית. עלות הקוונטיזציה כלולה במחיר ההמרה (החל מ-$200) ואינה מחויבת בנפרד.

הצוות שלנו סיפק בהצלחה המרות Core ML ליותר מ-20 לקוחות בתחום הפיננסים והקמעונאות. אנו מבטיחים שהדיוק לא יפגע ביותר מ-0.01 הפרש מקסימלי בהשוואה למודל המקורי. קבלו ייעוץ ממהנדס לגבי המודל שלכם — נעריך את מורכבות ההמרה ונבחר פרמטרים אופטימליים. צרו קשר לביקורת והמרה.

ציטוט עבור coremltools כלי ההמרה הרשמי של Apple: https://github.com/apple/coremltools