ML მოდელების კონვერტაცია Apple-ის მოწყობილობებზე: Core ML-ისა და MLX-ის გამოყენება `dots.ocr`-ის მაგალითზე
სტატია დეტალურად აღწერს, თუ როგორ ხდება მანქანური სწავლების (ML) მოდელების, კერძოდ `dots.ocr`-ის, ოპტიმიზაცია Apple-ის მოწყობილობებზე გასაშვებად. განხილულია Apple-ის ნეირონული ძრავის ენერგოეფექტურობა და Core ML-ის, როგორც მასზე წვდომის ერთადერთი საშუალების, შეზღუდვები. ასევე წარმოდგენილია MLX, უფრო მოქნილი ფრეიმვორკი, რომელიც GPU-ს იყენებს. სერიის პირველი ნაწილი მოიცავს `dots.ocr`-ის PyTorch-დან Core ML-ზე კონვერტაციის პროცესს, წარმოქმნილ პრობლემებს და მათ გადაჭრის გზებს, რათა დეველოპერებს დაეხმ
წარმოგიდგენთ ნეირონულ ძრავას (Neural Engine) – Apple-ის საკუთრივ შექმნილ ხელოვნური ინტელექტის ამაჩქარებელს, რომელიც 2017 წლიდან მოყოლებული Apple-ის ყველა მოწყობილობას მოყვება. ეს ამაჩქარებელი შექმნილია მაღალი წარმადობისთვის, ბატარეის ენერგიის მინიმალური მოხმარებით. ჩვენმა ზოგიერთმა ტესტმა აჩვენა, რომ ნეირონული ძრავა 12-ჯერ უფრო ენერგოეფექტურია, ვიდრე ცენტრალური პროცესორი (CPU), და 4-ჯერ უფრო ენერგოეფექტურია, ვიდრე გრაფიკული პროცესორი (GPU).
მიუხედავად იმისა, რომ ეს ყველაფერი ძალიან მიმზიდველად ჟღერს, სამწუხაროდ, ნეირონული ძრავა ხელმისაწვდომია მხოლოდ Core ML-ის, Apple-ის დახურული კოდის მანქანური სწავლების (ML) ფრეიმვორკის მეშვეობით. უფრო მეტიც, მოდელის PyTorch-დან Core ML-ზე უბრალო კონვერტაციაც კი შეიძლება გარკვეულ სირთულეებთან იყოს დაკავშირებული და წინასწარ კონვერტირებული მოდელის ან კონკრეტული ნიუანსების ცოდნის გარეშე, ეს პროცესი დამღლელი შეიძლება აღმოჩნდეს დეველოპერებისთვის.
საბედნიეროდ, Apple ასევე გვთავაზობს MLX-ს – უფრო თანამედროვე და მოქნილ ML ფრეიმვორკს, რომელიც მიზნად ისახავს GPU-ს (და არა ნეირონულ ძრავას) და მისი გამოყენება შესაძლებელია Core ML-თან ერთად.
ამ სამნაწილიან სერიაში, ჩვენ წარმოგიდგენთ დეტალურ მსჯელობას იმის შესახებ, თუ როგორ გადავიყვანეთ `dots.ocr` მოწყობილობაზე გასაშვებად, Core ML-ისა და MLX-ის კომბინაციის გამოყენებით. ეს პროცესი მრავალი სხვა მოდელისთვისაც გამოსადეგი უნდა იყოს და ვიმედოვნებთ, რომ ის დაეხმარება დეველოპერებს, რომლებიც საკუთარი მოდელების მოწყობილობაზე გაშვებას ცდილობენ, საჭირო იდეებისა და ხელსაწყოების იდენტიფიცირებაში.
მიყევით ინსტრუქციას: კლონირეთ რეპოზიტორია. `setup` ბრძანების გასაშვებად დაგჭირდებათ `uv` და `hf` ინსტალაცია. თუ უბრალოდ გსურთ, გამოტოვოთ პროცესი და გამოიყენოთ კონვერტირებული მოდელი, მისი ჩამოტვირთვა შეგიძლიათ აქ.
PyTorch-დან CoreML-ზე კონვერტაცია ორეტაპიანი პროცესია: მიუხედავად იმისა, რომ მეორე ეტაპისთვის გვაქვს რამდენიმე პარამეტრი, რომელთა რეგულირებაც შეგვიძლია, ჩვენი კონტროლის უმეტესი ნაწილი პირველ ეტაპზეა – ეს არის გრაფიკი, რომელსაც `coremltools`-ს ვაწვდით. პროგრამისტების ცნობილი პრინციპის – „ამუშავე, გაასწორე, დააჩქარე“ – გათვალისწინებით, პირველ რიგში, ყურადღებას გავამახვილებთ კონვერტაციის ამუშავებაზე GPU-ზე, FLOAT32 სიზუსტით და სტატიკური ფორმებით. მას შემდეგ, რაც ამას მივაღწევთ, შეგვიძლია შევამციროთ სიზუსტე და ვეცადოთ, გადავიდეთ ნეირონულ ძრავაზე.
`Dots.OCR` ორი ძირითადი კომპონენტისგან შედგება: 1.2 მილიარდი პარამეტრის მქონე ვიზუალური ენკოდერი, ნულიდან გაწვრთნილი NaViT არქიტექტურაზე დაფუძნებით, და Qwen2.5-1.5B ბექბოუნი. ვიზუალური ენკოდერის გასაშვებად გამოვიყენებთ CoreML-ს, ხოლო LM ბექბოუნის გასაშვებად – MLX-ს. მოდელის კონვერტაციის დასაწყებად, უმჯობესია, ჯერ გავიგოთ მისი სტრუქტურა და ფუნქციონირება. თუ გადავხედავთ ორიგინალურ ვიზუალური მოდელირების ფაილს აქ, დავინახავთ, რომ ვიზუალური ენკოდერი QwenVL ოჯახის მსგავსია. მრავალი ვიზუალური ენკოდერის მსგავსად, `dots`-ის ვიზუალური ენკოდერი მუშაობს პატჩების საფუძველზე, ამ შემთხვევაში 14x14 პატჩებით. `dots`-ის ვიზუალური ენკოდერს შეუძლია ვიდეოების და სურათების პარტიების დამუშავება. ეს გვაძლევს შესაძლებლობას, გავამარტივოთ პროცესი ერთდროულად მხოლოდ ერთი სურათის დამუშავებით. ეს მიდგომა ხშირია მოწყობილობაზე არსებულ აპებში, სადაც ჩვენ ვაკონვერტირებთ მოდელს, რომელიც უზრუნველყოფს არსებით ფუნქციებს და შემდეგ ვიმეორებთ პროცესს, თუ გვსურს მრავალი სურათის დამუშავება.
კონვერტაციის პროცესის დაწყებისას, უმჯობესია, მინიმალური სიცოცხლისუნარიანი მოდელით დავიწყოთ. ეს ნიშნავს ნებისმიერი ზედმეტი ფუნქციონალის მოცილებას, რომელიც მოდელის ფუნქციონირებისთვის აუცილებელი არ არის. ჩვენს შემთხვევაში, `dots`-ს აქვს ყურადღების (attention) მრავალი განსხვავებული იმპლემენტაცია, როგორც ვიზუალური ენკოდერისთვის, ასევე LM ბექბოუნისთვის. CoreML-ს აქვს ბევრი ინფრასტრუქტურა, რომელიც ორიენტირებულია `scaled_dot_product_attention` ოპერატორზე, რომელიც iOS 18-ში დაინერგა. ჩვენ შეგვიძლია გავამარტივოთ მოდელი ყველა სხვა ყურადღების იმპლემენტაციის მოცილებით და ამ ეტაპზე ფოკუსირება მოვახდინოთ მხოლოდ მარტივ `sdpa`-ზე (არა მეხსიერების ეფექტურ ვარიანტზე), იხილეთ ქომითი აქ.
მას შემდეგ, რაც ამას გავაკეთებთ, მოდელის ჩატვირთვისას ვნახავთ შემაშინებელ გამაფრთხილებელ შეტყობინებას: „The model doesn't require Sliding Window Attention to function, so we can happily move on.“ („მოდელი არ საჭიროებს მოძრავი ფანჯრის ყურადღებას (Sliding Window Attention) ფუნქციონირებისთვის, ამიტომ შეგვიძლია თამამად გავაგრძელოთ“).
`torch.jit.trace`-ის გამოყენება კვლავ ყველაზე მოწიფული მეთოდია მოდელების CoreML-ზე კონვერტაციისთვის. ჩვენ, როგორც წესი, ამას ვათავსებთ მარტივ „ჰარნესში“ (გარემოში), რომელიც საშუალებას გაძლევთ შეცვალოთ გამოყენებული გამოთვლითი ერთეულები და არჩეული სიზუსტე. საწყისი „ჰარნესის“ ნახვა შეგიძლიათ აქ. თუ ამას გავუშვებთ ორიგინალურ კოდის იმპლემენტაციაზე, პირველ (მრავალთაგან) პრობლემას შევეჯახებით. იშვიათია, რომ მოდელი პირველივე ცდაზე კონვერტირდეს. ხშირად დაგჭირდებათ ცვლილებების თანდათანობით შეტანა შესრულების გრაფის სიღრმეში, სანამ საბოლოო კვანძს არ მიაღწევთ.
ჩვენი პირველი პრობლემა შემდეგი შეცდომაა: „(მოცემული შეცდომის ტექსტი)“. საბედნიეროდ, ეს შეცდომა საკმაო ინფორმაციას გვაწვდის. ჩვენ შეგვიძლია გადავხედოთ `VisionRotaryEmbedding` ფენას და ვნახოთ შემდეგი კოდი: „(მოცემული კოდი)“. მიუხედავად იმისა, რომ `torch.arange`-ს აქვს `dtype` არგუმენტი, `coremltools` ამას `arange`-ისთვის უგულებელყოფს და ყოველთვის `int32`-ს გამოაქვს. ამ პრობლემის გამოსასწორებლად, შეგვიძლია უბრალოდ `arange`-ის შემდეგ დავამატოთ `cast`, იხილეთ ქომითი აქ.
ამის გამოსწორების შემდეგ, კონვერტაციის ხელახლა გაშვება მიგვიყვანს შემდეგ პრობლემასთან `repeat_interleave`-ში: „(მოცემული შეცდომის ტექსტი)“. მიუხედავად იმისა, რომ ეს შეცდომა ნაკლებად ინფორმატიულია, ჩვენს ვიზუალურ ენკოდერში `repeat_interleave`-ის მხოლოდ ერთი გამოძახება გვაქვს. `cu_seqlens` გამოიყენება ცვლადი სიგრძის მიმდევრობების დასაფარად `flash_attention_2`-ში. ის გამომდინარეობს `grid_thw` ტენზორიდან, რომელიც წარმოადგენს დროს, სიმაღლეს და სიგანეს. რადგან ჩვენ მხოლოდ ერთ სურათს ვამუშავებთ, შეგვიძლია უბრალოდ წავშალოთ ეს გამოძახება, იხილეთ ქომითი აქ.
გავაგრძელოთ! ამჯერად, უფრო გაუგებარ შეცდომას ვიღებთ: „(მოცემული შეცდომის ტექსტი)“. ეს კვლავ ცვლადი სიგრძის მიმდევრობების დამუშავების ნიღბის (masking) ლოგიკის გამო ხდება. რადგან ჩვენ მხოლოდ ერთ სურათს ვამუშავებთ (და არა ვიდეოს ან სურათების პარტიას...
წყარო: huggingface.co
AI-ით გადამუშავებული