გაიცანით ნერვული პროცესორი (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-ის კომბინაციის გამოყენებით. ეს პროცესი მრავალი სხვა მოდელისთვისაც გამოსადეგი უნდა იყოს და ვიმედოვნებთ, რომ ეს დაეხმარება დეველოპერებს, რომლებიც საკუთარი მოდელების მოწყობილობაზე გაშვებას ცდილობენ, გაიგონ საჭირო იდეები და ინსტრუმენტები. იმისათვის, რომ გაყვეთ ინსტრუქციებს, კლონირეთ რეპოზიტორიუმი (repo). დაგჭირდებათ uv და hf დაინსტალირებული setup ბრძანების გასაშვებად: თუ გსურთ უბრალოდ გამოტოვოთ ეს ნაწილი და გამოიყენოთ კონვერტირებული მოდელი, შეგიძლიათ ჩამოტვირთოთ ის აქედან. PyTorch-დან Core ML-ზე კონვერტაცია ორეტაპიანი პროცესია: მიუხედავად იმისა, რომ მეორე ეტაპისთვის გვაქვს რამდენიმე რეგულირებადი პარამეტრი, ჩვენი კონტროლის უმეტესი ნაწილი პირველ ეტაპზეა – ეს არის გრაფიკი, რომელსაც coremltools-ს ვაწვდით. პროგრამისტების ცნობილი ფრაზის – 'აქციე მუშადად, აქციე სწორად, აქციე სწრაფად' – მიხედვით, ჩვენ თავდაპირველად ყურადღებას გავამახვილებთ კონვერტაციის GPU-ზე, FLOAT32 სიზუსტით და სტატიკური ფორმებით ამუშავებაზე. როგორც კი ამას მივაღწევთ, შეგვიძლია შევამციროთ სიზუსტე და ვეცადოთ ნერვულ პროცესორზე გადასვლას. Dots.OCR შედგება ორი ძირითადი კომპონენტისგან: 1.2 მილიარდი პარამეტრის მქონე ვიზუალური ენკოდერი, რომელიც თავიდანვე არის გაწვრთნილი NaViT არქიტექტურაზე დაყრდნობით, და Qwen2.5-1.5B ხერხემალი (backbone). ვიზუალური ენკოდერის გასაშვებად გამოვიყენებთ Core ML-ს, ხოლო ენობრივი მოდელის (LM) ხერხემლის გასაშვებად – MLX-ს. მოდელის კონვერტაციის დასაწყებად, უმჯობესია თავდაპირველად მისი სტრუქტურა და ფუნქციონირება გავიგოთ. თუ გადავხედავთ ორიგინალ ვიზუალური მოდელირების ფაილს, დავინახავთ, რომ ვიზუალური ენკოდერი QwenVL ოჯახის მსგავსია. მრავალი ვიზუალური ენკოდერის მსგავსად, dots-ის ვიზუალური ენკოდერი მუშაობს პაჩების (patches) საფუძველზე, ამ შემთხვევაში 14x14 პაჩებზე. dots-ის ვიზუალური ენკოდერს შეუძლია ვიდეოების და სურათების პარტიების (batches) დამუშავება. ეს გვაძლევს გამარტივების შესაძლებლობას, რადგან შეგვიძლია მხოლოდ ერთ სურათზე ვიმუშაოთ ერთდროულად. ეს მიდგომა ხშირად გამოიყენება მოწყობილობაზე გაშვებულ აპლიკაციებში, სადაც ჩვენ ვაკონვერტირებთ მოდელს, რომელიც აუცილებელ ფუნქციებს უზრუნველყოფს და ვაუმჯობესებთ მას, თუ გვსურს მრავალი სურათის დამუშავება. კონვერტაციის პროცესის დაწყებისას, უმჯობესია დავიწყოთ მინიმალური სიცოცხლისუნარიანი მოდელით. ეს გულისხმობს ნებისმიერი ზედმეტი ფუნქციის მოცილებას, რომლებიც მკაცრად არ არის აუცილებელი მოდელის ფუნქციონირებისთვის. ჩვენს შემთხვევაში, dots-ს აქვს ყურადღების (attention) მრავალი განსხვავებული იმპლემენტაცია, როგორც ვიზუალური ენკოდერისთვის, ასევე LM ხერხემლისთვის. Core ML-ს აქვს ვრცელი ინფრასტრუქტურა, რომელიც ორიენტირებულია `scaled_dot_product_attention` ოპერატორზე, რომელიც iOS 18-ში დაინერგა. ჩვენ შეგვიძლია მოდელის გამარტივება ყურადღების ყველა სხვა იმპლემენტაციის მოცილებით და ამ ეტაპზე მხოლოდ მარტივ sdpa-ზე (არა მეხსიერების ეფექტურ ვარიანტზე) ფოკუსირებით, იხილეთ ცვლილება აქ. მას შემდეგ, რაც ამას გავაკეთებთ, მოდელის ჩატვირთვისას ვნახავთ საგანგაშო გამაფრთხილებელ შეტყობინებას: 'The model doesn't require Sliding Window Attention to function, so we can happily move on.' `torch.jit.trace`-ის გამოყენება კვლავ ყველაზე გამართული მეთოდია Core ML-ზე მოდელების კონვერტაციისთვის. ჩვენ, როგორც წესი, ამას ვახვევთ მარტივ დამხმარე კოდში (harness), რომელიც საშუალებას გაძლევთ შეცვალოთ გამოყენებული გამომთვლელი ერთეულები და არჩეული სიზუსტე. საწყისი დამხმარე კოდის ნახვა შეგიძლიათ აქ. თუ ორიგინალ კოდის იმპლემენტაციაზე შემდეგს გავუშვებთ: ჩვენ პირველ (მრავალიდან) პრობლემას წავაწყდებით. იშვიათია, რომ მოდელი პირველივე ცდაზე კონვერტირდეს. ხშირად, თქვენ მოგიწევთ ეტაპობრივად ცვლილებების შეტანა შესრულების გრაფიკის უფრო და უფრო ღრმა ნაწილებში, სანამ საბოლოო კვანძს არ მიაღწევთ. ჩვენი პირველი პრობლემა არის შემდეგი შეცდომა: საბედნიეროდ, ეს შეცდომა საკმაოდ ბევრ ინფორმაციას გვაძლევს. შეგვიძლია გადავხედოთ VisionRotaryEmbedding ფენას და ვნახოთ შემდეგი კოდი: მიუხედავად იმისა, რომ `torch.arange`-ს აქვს `dtype` არგუმენტი, coremltools ამას არგუმენტისთვის (arange) უგულებელყოფს და ყოველთვის `int32`-ს აბრუნებს. ამ პრობლემის გამოსასწორებლად, შეგვიძლია უბრალოდ დავამატოთ 'cast' `arange`-ის შემდეგ, იხილეთ ცვლილება აქ. ამ პრობლემის გამოსწორების შემდეგ, კონვერტაციის ხელახლა გაშვება მივყავართ შემდეგ პრობლემამდე `repeat_interleave`-თან დაკავშირებით: მიუხედავად იმისა, რომ ეს შეცდომა ნაკლებად ინფორმატიულია, ჩვენს ვიზუალურ ენკოდერში მხოლოდ ერთი `repeat_interleave` გამოძახება გვაქვს: `cu_seqlens` გამოიყენება ცვლადი სიგრძის მიმდევრობების დასაფარად (masking) `flash_attention_2`-ში. ის მიღებულია `grid_thw` ტენსორიდან, რომელიც წარმოადგენს დროს, სიმაღლესა და სიგანეს. რადგან ჩვენ მხოლოდ ერთ სურათს ვამუშავებთ, შეგვიძლია უბრალოდ მოვაცილოთ ეს გამოძახება, იხილეთ ცვლილება აქ. შემდეგზე გადავიდეთ! ამჯერად, უფრო კრიპტიკულ შეცდომას ვიღებთ: ეს კვლავ განპირობებულია ცვლადი სიგრძის მიმდევრობების დასამუშავებლად განკუთვნილი დამფარავი ლოგიკით (masking logic). ვინაიდან ჩვენ მხოლოდ ერთ სურათს ვამუშავებთ (და არა ვიდეოს ან სურათების პარტიას...