├── .gitmodules
├── huggingface-ios-app
├── huggingface-ios-app
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── ViewController.swift
│ ├── Info.plist
│ ├── Base.lproj
│ │ ├── Main.storyboard
│ │ └── LaunchScreen.storyboard
│ ├── AppDelegate.swift
│ └── SceneDelegate.swift
├── huggingface-ios-app.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ ├── xcuserdata
│ │ └── danielvela.xcuserdatad
│ │ │ ├── xcschemes
│ │ │ └── xcschememanagement.plist
│ │ │ └── xcdebugger
│ │ │ └── Breakpoints_v2.xcbkptlist
│ ├── xcshareddata
│ │ └── xcschemes
│ │ │ └── huggingface-ios-app.xcscheme
│ └── project.pbxproj
├── Sources
│ ├── BartModel.swift
│ ├── DialogGPTModel.swift
│ ├── Utils.swift
│ ├── T5SmallModel.swift
│ ├── GPT2Model.swift
│ ├── Math.swift
│ ├── GPT2Tokenizer.swift
│ ├── BartTokenizer.swift
│ ├── MLMultiArray+Utils.swift
│ └── DistilGPT2Tokenizer.swift
├── huggingface-ios-appTests
│ └── Distilgpt2Tests.swift
└── Resources
│ └── models
│ └── facebook
│ └── blenderbot-400M-distill
│ └── merges.txt
├── .gitignore
├── try.py
├── create-jit.py
├── create-chat-model.py
├── readme.md
└── search-models.py
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "exporters"]
2 | path = exporters
3 | url = https://github.com/huggingface/exporters.git
4 |
--------------------------------------------------------------------------------
/huggingface-ios-app/huggingface-ios-app/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /models
2 | /exported
3 | /huggingface-ios-app/Resources/models
4 |
5 | ## User settings
6 | xcuserdata/
7 |
8 | ## Xcode 8 and earlier
9 | *.xcscmblueprint
10 | *.xccheckout
11 |
12 | *.mlpackage
13 |
14 |
--------------------------------------------------------------------------------
/huggingface-ios-app/huggingface-ios-app.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/huggingface-ios-app/huggingface-ios-app/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/huggingface-ios-app/huggingface-ios-app/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/huggingface-ios-app/huggingface-ios-app.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/huggingface-ios-app/huggingface-ios-app/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // huggingface-ios-app
4 | //
5 | // Created by Daniel Vela on 10/5/23.
6 | //
7 |
8 | import UIKit
9 |
10 | class ViewController: UIViewController {
11 |
12 | override func viewDidLoad() {
13 | super.viewDidLoad()
14 | // Do any additional setup after loading the view.
15 | }
16 |
17 |
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/huggingface-ios-app/huggingface-ios-app/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | UIApplicationSceneManifest
6 |
7 | UIApplicationSupportsMultipleScenes
8 |
9 | UISceneConfigurations
10 |
11 | UIWindowSceneSessionRoleApplication
12 |
13 |
14 | UISceneConfigurationName
15 | Default Configuration
16 | UISceneDelegateClassName
17 | $(PRODUCT_MODULE_NAME).SceneDelegate
18 | UISceneStoryboardFile
19 | Main
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/huggingface-ios-app/huggingface-ios-app.xcodeproj/xcuserdata/danielvela.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | huggingface-ios-app.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 0BB76E862A0B9E6C000D9187
16 |
17 | primary
18 |
19 |
20 | 0BB76E9C2A0B9E6D000D9187
21 |
22 | primary
23 |
24 |
25 | 0BB76EA62A0B9E6D000D9187
26 |
27 | primary
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/try.py:
--------------------------------------------------------------------------------
1 | from transformers import BlenderbotTokenizer
2 | from transformers import BlenderbotForConditionalGeneration
3 |
4 | def get_models():
5 | # pytorch keeps an internal state of the conversation
6 | model_name = "facebook/blenderbot-400M-distill"
7 | # model_name = "facebook/blenderbot-1B-distill"
8 | # model_name = "facebook/blenderbot-90M"
9 | tokenizer = BlenderbotTokenizer.from_pretrained(model_name, cache_dir="./models")
10 | model = BlenderbotForConditionalGeneration.from_pretrained(model_name)
11 | return tokenizer, model
12 |
13 | def generate_answer():
14 | tokenizer, model = get_models()
15 | print(tokenizer(" Hello world")["input_ids"])
16 | # Ask user for input from command line
17 | user_message = input(">> ")
18 | inputs = tokenizer(user_message, return_tensors="pt")
19 | result = model.generate(**inputs)
20 | message_bot = tokenizer.decode(
21 | result[0], skip_special_tokens=True
22 | ) # .replace("", "").replace("", "")
23 |
24 | print(message_bot)
25 |
26 |
27 | print("Talk to the bot")
28 |
29 | while True:
30 | generate_answer()
--------------------------------------------------------------------------------
/create-jit.py:
--------------------------------------------------------------------------------
1 | import torch
2 | from transformers import AutoTokenizer
3 | from transformers import AutoModelForSeq2SeqLM, AutoModelForCausalLM, GPT2LMHeadModel, AutoModel
4 |
5 |
6 | # Load "microsoft/DialoGPT-small"
7 | # torch_model = AutoModelForCausalLM.from_pretrained("microsoft/DialoGPT-small", torchscript=True)
8 | torch_model = AutoModelForCausalLM.from_pretrained("facebook/blenderbot_small-90M", torchscript=True)
9 | # Set the model in evaluation mode.
10 | torch_model.eval()
11 |
12 | # Trace the model with random data.
13 | # tensor of random ints
14 | example_input = torch.randint(0, 100, (1,128), dtype=torch.int64)
15 | print(example_input.shape)
16 | print(example_input)
17 | traced_model = torch.jit.trace(torch_model, example_input)
18 | out = traced_model(example_input)
19 | print(out[0].shape)
20 |
21 |
22 |
23 | import coremltools as ct
24 | import coremltools.models.datatypes as datatypes
25 | sequence_length = 64
26 | array_input = datatypes.Array(sequence_length)
27 |
28 | # Define inputs and outputs for the Core ML model.
29 | input_shape = ct.Shape(shape=(1,
30 | 3,
31 | ct.RangeDim(lower_bound=25, upper_bound=100, default=45),
32 | ct.RangeDim(lower_bound=25, upper_bound=100, default=45)))
33 |
34 |
35 | # Using image_input in the inputs parameter:
36 | # Convert to Core ML program using the Unified Conversion API.
37 | model = ct.convert(
38 | traced_model,
39 | inputs=[ct.TensorType(shape=example_input.shape, name="input")],
40 | # outputs=[ct.TensorType(name="output")],
41 | convert_to="mlprogram"
42 | )
43 |
44 | # Save the converted model.
45 | model.save("t5-small.mlpackage")
--------------------------------------------------------------------------------
/huggingface-ios-app/Sources/BartModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BartModel.swift
3 | // huggingface-ios-app
4 | //
5 | // Created by Daniel Vela on 12/5/23.
6 | //
7 |
8 | import Foundation
9 | import CoreML
10 |
11 | class BartModel {
12 | let encoder: bart_large_encoder
13 | // let decoder: bart_large_decoder
14 | let tokenizer: DistilGPT2Tokenizer
15 | let seqLen = 128
16 |
17 | init() throws {
18 | let modelName = "facebook/bart-large"
19 | self.tokenizer = try DistilGPT2Tokenizer.from_pretrained(modelName)
20 | self.encoder = try bart_large_encoder()
21 | // self.decoder = try bart_large_decoder()
22 | }
23 |
24 | func predict(_ string: String) throws -> String {
25 | let tokens = tokenizer.encode(text: string)
26 |
27 | let maxTokens = (tokens.count > seqLen)
28 | ? Array(tokens[..
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/huggingface-ios-app/huggingface-ios-app/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/create-chat-model.py:
--------------------------------------------------------------------------------
1 | from exporters.coreml import export, validate_model_outputs, CoreMLConfig
2 | from exporters.coreml.models import *
3 | from transformers import AutoTokenizer
4 | from transformers import AutoModelForSeq2SeqLM, AutoModelForCausalLM
5 |
6 |
7 | class DialogGPT2CoreMLConfig(GPT2CoreMLConfig):
8 | @property
9 | def inputs(self) -> OrderedDict[str, InputDescription]:
10 | input_descs = super().inputs
11 | input_descs["input_ids"].sequence_length = 64
12 | return input_descs
13 |
14 | feature = "text2text-generation"
15 | # feature = "text-generation"
16 |
17 | models = [
18 | # ("Alethea/GPT2-chitchat", DialogGPT2CoreMLConfig, AutoModelForCausalLM),
19 | ("microsoft/DialoGPT-small", DialogGPT2CoreMLConfig, AutoModelForCausalLM)
20 | # ("microsoft/DialoGPT-medium", DialogGPT2CoreMLConfig, AutoModelForCausalLM)
21 | # ("microsoft/DialoGPT-large", DialogGPT2CoreMLConfig, AutoModelForCausalLM)
22 | # ("google/flan-t5-small", T5CoreMLConfig, AutoModelForSeq2SeqLM)
23 | ]
24 |
25 | for model_ckpt, config_class, auto_model in models:
26 | print("--------------------------------")
27 | print(" EXPORTING: ", model_ckpt)
28 | base_model = auto_model.from_pretrained(
29 | model_ckpt, torchscript=True
30 | )
31 | preprocessor = AutoTokenizer.from_pretrained(model_ckpt)
32 |
33 | coreml_config = config_class(
34 | base_model.config,
35 | task=feature,
36 | use_past=False
37 | )
38 | mlmodel = export(
39 | preprocessor, base_model, coreml_config
40 | )
41 | validate_model_outputs(
42 | coreml_config,
43 | preprocessor,
44 | base_model,
45 | mlmodel,
46 | coreml_config.atol_for_validation,
47 | )
48 | mlmodel.save(f"exported/{model_ckpt}.mlpackage")
49 |
50 | print("END --------------------------------")
51 |
--------------------------------------------------------------------------------
/huggingface-ios-app/huggingface-ios-app/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // huggingface-ios-app
4 | //
5 | // Created by Daniel Vela on 10/5/23.
6 | //
7 |
8 | import UIKit
9 |
10 | @main
11 | class AppDelegate: UIResponder, UIApplicationDelegate {
12 |
13 |
14 |
15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
16 | // Override point for customization after application launch.
17 | do {
18 | // let model = try T5SmallModel()
19 | // try model.generate(text: "Hola Mundo")
20 | let model = try GPT2Model()
21 | let result = model.generate(text: "Does money buy happiness?", nTokens: 128, callback: { string, double in
22 | // print(string)
23 | })
24 | print(result)
25 | } catch {
26 | print(error.localizedDescription)
27 | }
28 | return true
29 | }
30 |
31 | // MARK: UISceneSession Lifecycle
32 |
33 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
34 | // Called when a new scene session is being created.
35 | // Use this method to select a configuration to create the new scene with.
36 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
37 | }
38 |
39 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
40 | // Called when the user discards a scene session.
41 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
42 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
43 | }
44 |
45 |
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/huggingface-ios-app/Sources/DialogGPTModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DialogGPTModel.swift
3 | // huggingface-ios-app
4 | //
5 | // Created by Daniel Vela on 14/5/23.
6 | //
7 |
8 | import Foundation
9 | import CoreML
10 |
11 | class DialogGPTModel {
12 | let model: DialoGPT_small
13 | let seqLen = 12
14 |
15 | let eos_token_id = 50256
16 |
17 | init() throws {
18 | self.model = try DialoGPT_small()
19 | }
20 |
21 | func generate(_ string: String) throws -> String {
22 | var tokens = [13921, 1637, 2822, 12157, 30, 50256]
23 |
24 |
25 | for _ in 0..<6 {
26 | var next_token = try predict(tokens)
27 | tokens.append(next_token)
28 | print(String(next_token) + ", ")
29 | }
30 |
31 | return ""
32 | }
33 |
34 | private func predict(_ tokens: [Int]) throws -> Int {
35 | var prev_prob = 0.0
36 | var next_token = 0
37 | let maxTokens = (tokens.count > seqLen)
38 | ? Array(tokens[.. prev_prob {
61 | next_token = tok
62 | prev_prob = prob
63 | }
64 |
65 | }
66 |
67 | return next_token
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/huggingface-ios-app/Sources/Utils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Utils.swift
3 | // AudioBoloss
4 | //
5 | // Created by Julien Chaumond on 07/01/2019.
6 | // Copyright © 2019 Hugging Face. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct Utils {
12 | /// Time a block in ms
13 | static func time(label: String, _ block: () -> T) -> T {
14 | let startTime = CFAbsoluteTimeGetCurrent()
15 | let result = block()
16 | let diff = (CFAbsoluteTimeGetCurrent() - startTime) * 1_000
17 | print("[\(label)] \(diff)ms")
18 | return result
19 | }
20 |
21 | /// Time a block in seconds and return (output, time)
22 | static func time(_ block: () -> T) -> (T, Double) {
23 | let startTime = CFAbsoluteTimeGetCurrent()
24 | let result = block()
25 | let diff = CFAbsoluteTimeGetCurrent() - startTime
26 | return (result, diff)
27 | }
28 |
29 | /// Return unix timestamp in ms
30 | static func dateNow() -> Int64 {
31 | // Use `Int` when we don't support 32-bits devices/OSes anymore.
32 | // Int crashes on iPhone 5c.
33 | return Int64(Date().timeIntervalSince1970 * 1000)
34 | }
35 |
36 | /// Clamp a val to [min, max]
37 | static func clamp(_ val: T, _ vmin: T, _ vmax: T) -> T {
38 | return min(max(vmin, val), vmax)
39 | }
40 |
41 | /// Fake func that can throw.
42 | static func fakeThrowable(_ input: T) throws -> T {
43 | return input
44 | }
45 |
46 | /// Substring
47 | static func substr(_ s: String, _ r: Range) -> String? {
48 | let stringCount = s.count
49 | if stringCount < r.upperBound || stringCount < r.lowerBound {
50 | return nil
51 | }
52 | let startIndex = s.index(s.startIndex, offsetBy: r.lowerBound)
53 | let endIndex = s.index(startIndex, offsetBy: r.upperBound - r.lowerBound)
54 | return String(s[startIndex..(_ dict: Dictionary) -> Dictionary {
59 | var inverted: [V: K] = [:]
60 | for (k, v) in dict {
61 | inverted[v] = k
62 | }
63 | return inverted
64 | }
65 | }
66 |
67 |
--------------------------------------------------------------------------------
/huggingface-ios-app/huggingface-ios-app.xcodeproj/xcuserdata/danielvela.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
21 |
22 |
23 |
25 |
37 |
38 |
39 |
41 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/huggingface-ios-app/huggingface-ios-app/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // huggingface-ios-app
4 | //
5 | // Created by Daniel Vela on 10/5/23.
6 | //
7 |
8 | import UIKit
9 |
10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
11 |
12 | var window: UIWindow?
13 |
14 |
15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
19 | guard let _ = (scene as? UIWindowScene) else { return }
20 | }
21 |
22 | func sceneDidDisconnect(_ scene: UIScene) {
23 | // Called as the scene is being released by the system.
24 | // This occurs shortly after the scene enters the background, or when its session is discarded.
25 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
26 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
27 | }
28 |
29 | func sceneDidBecomeActive(_ scene: UIScene) {
30 | // Called when the scene has moved from an inactive state to an active state.
31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
32 | }
33 |
34 | func sceneWillResignActive(_ scene: UIScene) {
35 | // Called when the scene will move from an active state to an inactive state.
36 | // This may occur due to temporary interruptions (ex. an incoming phone call).
37 | }
38 |
39 | func sceneWillEnterForeground(_ scene: UIScene) {
40 | // Called as the scene transitions from the background to the foreground.
41 | // Use this method to undo the changes made on entering the background.
42 | }
43 |
44 | func sceneDidEnterBackground(_ scene: UIScene) {
45 | // Called as the scene transitions from the foreground to the background.
46 | // Use this method to save data, release shared resources, and store enough scene-specific state information
47 | // to restore the scene back to its current state.
48 | }
49 |
50 |
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Huggingface to CoreML
2 |
3 | This repository contains a script and information to convert a Huggingface models to CoreML.
4 |
5 | ## Info
6 | - Model to transform: [microsoft/DialoGPT-small](https://huggingface.co/microsoft/DialoGPT-small?text=Hi.) Try it by executing in command line `$ python3 create-chat-model.py`
7 | - Also create this [microsoft/DialoGPT-small](https://huggingface.co/microsoft/DialoGPT-small?text=Hi.)
8 |
9 | ### Links
10 | - [HuggingFace](https://huggingface.co/)
11 | - [HuggingFace Transformers](https://huggingface.co/transformers/)
12 | - [HuggingFace Core ML Models](https://huggingface.co/coreml#models)
13 | - [Using Stable Diffusion with Core ML on Apple Silicon](https://huggingface.co/blog/diffusers-coreml)
14 | - [Export Hugging Face models to Core ML and TensorFlow Lite](https://github.com/huggingface/exporters)
15 | - [Swift Core ML implementations of Transformers: GPT-2, DistilGPT-2, BERT, DistilBERT, more coming soon!](https://github.com/huggingface/swift-coreml-transformers)
16 | - [Figuring out the shape of a Transformer Model To translate it to a coreML model](https://developer.apple.com/forums/thread/682408)
17 | - [Core ML Stable Diffusion](https://github.com/apple/ml-stable-diffusion)
18 | - [Deploying Transformers on the Apple Neural Engine](https://machinelearning.apple.com/research/neural-engine-transformers)
19 | - [Swift app demonstrating Core ML Stable Diffusion](https://github.com/huggingface/swift-coreml-diffusers)
20 | - [Apple Core ML](https://developer.apple.com/documentation/coreml)
21 | -[BenderbotTokenizer doc](https://huggingface.co/docs/transformers/model_doc/blenderbot#transformers.BlenderbotTokenizer)
22 | - [BenderbotTokenizer implementation](https://github.com/huggingface/transformers/blob/3335724376319a0c453049d0cd883504f530ff52/src/transformers/models/blenderbot/tokenization_blenderbot.py#L4)
23 | - https://huggingface.co/microsoft/DialoGPT-small?text=Hi.
24 | - [Converting from PyTorch](https://coremltools.readme.io/docs/pytorch-conversion)
25 |
26 | ## Initial steps
27 |
28 | 0. Clone repo: `$ git clone https://github.com/madcato/huggingface-coreml.git`
29 | 1. Execute chatbot to try chatbot (optional), `$ python3 try.py`
30 | 2. Install huggingface exporters submodule,
31 | `$ git submodule init && git submodule update`
32 | 3. `$ cd exporters && pip3 install -e . && cd ..`
33 | 4. `python3 export`
34 |
35 | The last step will create two `mlpackage` files in the `exported` folder: one for decoder and another for decoder part of the model. These files can be opened with Xcode and the model can be tested in the playground or project.
36 |
37 | ### Try
38 |
39 | - `$ python3 -m exporters.coreml --model=t5-small --feature=text2text-generation exported`
40 | - `$ python3 -m exporters.coreml --model=distilgpt2 --feature=text2text-generation exported`
41 |
42 | ## Exporters features
43 |
44 | - 'feature-extraction',
45 | - 'feature-extraction-with-past',
46 | - 'fill-mask',
47 | - 'image-classification',
48 | - 'masked-im',
49 | - 'multiple-choice',
50 | - 'next-sentence-prediction',
51 | - 'object-detection',
52 | - 'question-answering',
53 | - 'semantic-segmentation',
54 | - 'text-classification',
55 | - 'text-generation',
56 | - 'text-generation-with-past',
57 | - 'text2text-generation',
58 | - 'token-classification',
59 | - 'sequence-classification',
60 | - 'causal-lm',
61 | - 'causal-lm-with-past',
62 | - 'seq2seq-lm',
63 | - 'seq2seq-lm-with-past',
64 | - 'speech2seq-lm',
65 | - 'speech2seq-lm-with-past',
66 | - 'masked-lm',
67 | - 'vision2seq-lm',
68 | - 'default',
69 | - 'default-with-past',
70 | - 'automatic-speech-recognition',
71 | - 'ctc'
72 |
--------------------------------------------------------------------------------
/huggingface-ios-app/Sources/T5SmallModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // T5SmallModel.swift
3 | // huggingface-ios-app
4 | //
5 | // Created by Daniel Vela on 13/5/23.
6 | //
7 |
8 | import Foundation
9 | import CoreML
10 |
11 | class T5SmallModel {
12 | let encoder: encoder_t5_small
13 | let decoder: decoder_t5_small
14 | let seqLen = 64
15 |
16 | init() throws {
17 | self.encoder = try encoder_t5_small()
18 | self.decoder = try decoder_t5_small()
19 | }
20 |
21 | enum DecodingStrategy {
22 | /// At each time step, we select the most likely next token
23 | case greedy
24 | /// Sample only from the top-k most-probable tokens (k is a hyper-parameter).
25 | case topK(Int)
26 | /// Sample from the top tokens with a cumulative probability just above a threshold (nucleus/top-p).
27 | case topP(Double)
28 | }
29 |
30 | private let strategy = DecodingStrategy.greedy
31 |
32 | func predict(_ tokens: [Int]) throws -> Int {
33 | // let tokens = tokenizer.encode(text: string)
34 | // let tokens = [1] + tokens_0 + [2]
35 | // let tokens = [8774, 296, 149, 33, 25, 58, 1, 0 , 0, 0, 0, 0, 0,0, 0,0 ,0, 0,0 ,0, 0, 0, 0,0,0,0,0,0,0,0,0,0,0,0]
36 |
37 |
38 | let maxTokens = (tokens.count > seqLen)
39 | ? Array(tokens[.. String {
78 | // var tokens = tokenizer.encode(text: text)
79 | var tokens = [363, 31, 7, 39, 564, 58, 1]
80 | var newTokens: [Int] = []
81 | for i in 0.. seqLen)
60 | ? Array(tokens[..
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
34 |
40 |
41 |
42 |
45 |
51 |
52 |
53 |
54 |
55 |
65 |
67 |
73 |
74 |
75 |
76 |
82 |
84 |
90 |
91 |
92 |
93 |
95 |
96 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/search-models.py:
--------------------------------------------------------------------------------
1 | from exporters.coreml import export, validate_model_outputs, CoreMLConfig
2 | from exporters.coreml.models import *
3 | from transformers import AutoTokenizer
4 | from transformers import AutoModelForSeq2SeqLM, AutoModelForCausalLM, GPT2Model
5 |
6 | feature = "text2text-generation"
7 |
8 | models = [
9 | # ("npc-engine/exported-bart-light-gail-chatbot", BartCoreMLConfig), # not working
10 | # ("shahules786/Safetybot-T5-base", T5CoreMLConfig),
11 | # ("osanseviero/t5-finetuned-test", T5CoreMLConfig),
12 | # ("BlackSamorez/rudialogpt3_medium_based_on_gpt2_2ch", GPT2CoreMLConfig), # Error coniguracion
13 | # ("gorkemgoknar/gpt2chatbotenglish", GPT2CoreMLConfig), # Error coniguracion
14 | # ("Alethea/GPT2-chitchat", GPT2CoreMLConfig), # Error coniguracion
15 | # ("thu-coai/CDial-GPT2_LCCC-base", GPT2CoreMLConfig), # Error modelo
16 | # ("robinhad/gpt2-uk-conversational", GPT2CoreMLConfig), # Error coniguracion
17 | # ("AriakimTaiyo/gpt2-chat", GPT2CoreMLConfig), # Error coniguracion
18 | # ("Vaibhav-rm/GPT2-Shri-v1", GPT2CoreMLConfig), # Error coniguracion
19 | # ("LrxLcs/GPT2-V2", GPT2CoreMLConfig), # Error coniguracion
20 | # ("LrxLcs/GPT2-Test", GPT2CoreMLConfig), # Error coniguracion
21 | # ("huolongguo10/CDial-GPT2-LCCC-Base-copy", GPT2CoreMLConfig), # Error coniguracion
22 | # ("h2oai/h2ogpt-oasst1-512-12b", GPT2CoreMLConfig), # Error coniguracion
23 | # ("distilgpt2", GPT2CoreMLConfig), # Error coniguracion
24 | ("microsoft/DialoGPT-small", GPT2CoreMLConfig), # Error coniguracion
25 | # ("gorkemgoknar/gpt2chatbotenglish", GPT2CoreMLConfig)
26 | ("distilgpt2", GPT2CoreMLConfig),
27 | ("ethzanalytics/distilgpt2-tiny-conversational", GPT2CoreMLConfig)
28 | ]
29 |
30 | for model_ckpt, config_class in models:
31 | print("--------------------------------")
32 | print(" EXPORTING: ", model_ckpt)
33 | base_model = GPT2Model.from_pretrained(
34 | model_ckpt, torchscript=True
35 | )
36 | preprocessor = AutoTokenizer.from_pretrained(model_ckpt)
37 |
38 |
39 | print(" ENCODER --------------------------------")
40 | coreml_config = config_class(
41 | base_model.config,
42 | task="text2text-generation",
43 | use_past=False,
44 | # seq2seq="encoder"
45 | )
46 | mlmodel = export(
47 | preprocessor, base_model, coreml_config
48 | )
49 | # validate_model_outputs(
50 | # coreml_config,
51 | # preprocessor,
52 | # base_model,
53 | # mlmodel,
54 | # coreml_config.atol_for_validation,
55 | # )
56 | mlmodel.save(f"exported/{model_ckpt}.mlpackage")
57 |
58 | # print(" DECODER --------------------------------")
59 | # coreml_config = config_class(
60 | # base_model.config,
61 | # task="text2text-generation",
62 | # use_past=False,
63 | # seq2seq="decoder"
64 | # )
65 | # mlmodel = export(
66 | # preprocessor, base_model, coreml_config
67 | # )
68 | # validate_model_outputs(
69 | # coreml_config,
70 | # preprocessor,
71 | # base_model,
72 | # mlmodel,
73 | # coreml_config.atol_for_validation,
74 | # )
75 | # mlmodel.save(f"exported/{model_ckpt}_decoder.mlpackage")
76 | print("END --------------------------------")
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | # # Encoder
92 | # coreml_config = BlenderbotSmallCoreMLConfig(
93 | # base_model.config,
94 | # task="text2text-generation",
95 | # use_past=False,
96 | # seq2seq="encoder"
97 | # )
98 | # mlmodel = export(
99 | # preprocessor, base_model, coreml_config
100 | # )
101 |
102 | # mlmodel.save(f"exported/{model_ckpt}_encoder.mlpackage")
103 |
104 | # # Decoder
105 | # coreml_config = BlenderbotSmallCoreMLConfig(
106 | # base_model.config,
107 | # task="text2text-generation",
108 | # use_past=False,
109 | # seq2seq="decoder"
110 | # )
111 | # mlmodel = export(
112 | # preprocessor, base_model, coreml_config
113 | # )
114 |
115 | # mlmodel.save(f"exported/{model_ckpt}_decoder.mlpackage")
--------------------------------------------------------------------------------
/huggingface-ios-app/Sources/GPT2Model.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GPT2Model.swift
3 | // huggingface-ios-app
4 | //
5 | // Created by Daniel Vela on 14/5/23.
6 | //
7 |
8 | import Foundation
9 | import CoreML
10 |
11 | final class GPT2Model {
12 | private let model: DialoGPT_small
13 | private let tokenizer: GPT2Tokenizer
14 | private let seqLen = 64
15 | private let eos_token_id = 50256
16 |
17 | init() throws {
18 | let modelName = "distilgpt2"
19 | self.tokenizer = try GPT2Tokenizer.from_pretrained(modelName)
20 | self.model = try DialoGPT_small()
21 | }
22 |
23 | enum DecodingStrategy {
24 | /// At each time step, we select the most likely next token
25 | case greedy
26 | /// Sample only from the top-k most-probable tokens (k is a hyper-parameter).
27 | case topK(Int)
28 | /// Sample from the top tokens with a cumulative probability just above a threshold (nucleus/top-p).
29 | case topP(Double)
30 | }
31 |
32 | private let strategy = DecodingStrategy.topK(2)
33 |
34 | /// Main prediction loop:
35 | /// Predict next token from array of previous tokens.
36 | /// - featurization
37 | /// - model inference
38 | /// - Decoding according to the model's `strategy`
39 | func predict(tokens: [Int]) -> Int {
40 | let maxTokens = (tokens.count > seqLen)
41 | ? Array(tokens[.. Void)?) -> String {
95 | var tokens = tokenizer.encode(text: text) + [eos_token_id]
96 | var newTokens: [Int] = []
97 | for i in 0..", i, nextToken, tokens.count)
108 | callback?(
109 | tokenizer.decode(tokens: newTokens), time
110 | )
111 | }
112 | return tokenizer.decode(tokens: newTokens)
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/huggingface-ios-app/Sources/Math.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Math.swift
3 | // CoreMLBert
4 | //
5 | // Created by Julien Chaumond on 27/06/2019.
6 | // Copyright © 2019 Hugging Face. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Accelerate
11 | import CoreML
12 |
13 | ///
14 | /// From M.I. Hollemans
15 | ///
16 | /// https://github.com/hollance/CoreMLHelpers
17 | ///
18 | struct Math {
19 |
20 | /**
21 | Returns the index and value of the largest element in the array.
22 |
23 | - Parameters:
24 | - ptr: Pointer to the first element in memory.
25 | - count: How many elements to look at.
26 | - stride: The distance between two elements in memory.
27 | */
28 | static func argmax(_ ptr: UnsafePointer, count: Int, stride: Int = 1) -> (Int, Float) {
29 | var maxValue: Float = 0
30 | var maxIndex: vDSP_Length = 0
31 | vDSP_maxvi(ptr, vDSP_Stride(stride), &maxValue, &maxIndex, vDSP_Length(count))
32 | return (Int(maxIndex), maxValue)
33 | }
34 |
35 | /**
36 | Returns the index and value of the largest element in the array.
37 | - Parameters:
38 | - ptr: Pointer to the first element in memory.
39 | - count: How many elements to look at.
40 | - stride: The distance between two elements in memory.
41 | */
42 | static func argmax(_ ptr: UnsafePointer, count: Int, stride: Int = 1) -> (Int, Double) {
43 | var maxValue: Double = 0
44 | var maxIndex: vDSP_Length = 0
45 | vDSP_maxviD(ptr, vDSP_Stride(stride), &maxValue, &maxIndex, vDSP_Length(count))
46 | return (Int(maxIndex), maxValue)
47 | }
48 |
49 |
50 | /// MLMultiArray helper.
51 | /// Works in our specific use case.
52 | static func argmax(_ multiArray: MLMultiArray) -> (Int, Double) {
53 | assert(multiArray.dataType == .double)
54 | let ptr = UnsafeMutablePointer(OpaquePointer(multiArray.dataPointer))
55 | return Math.argmax(ptr, count: multiArray.count)
56 | }
57 |
58 | /// MLMultiArray helper.
59 | /// Works in our specific use case.
60 | static func argmax32(_ multiArray: MLMultiArray) -> (Int, Float) {
61 | assert(multiArray.dataType == .float32)
62 | let ptr = UnsafeMutablePointer(OpaquePointer(multiArray.dataPointer))
63 | let count = multiArray.count
64 | var maxValue: Float = 0
65 | var maxIndex: vDSP_Length = 0
66 | vDSP_maxvi(ptr, vDSP_Stride(1), &maxValue, &maxIndex, vDSP_Length(count))
67 | return (Int(maxIndex), maxValue)
68 | }
69 |
70 | /// Top-K.
71 | /// Select the k most-probable elements indices from `arr`
72 | /// and return both the indices (from the original array)
73 | /// and their softmaxed probabilities.
74 | ///
75 | static func topK(arr: [Float], k: Int) -> (indexes: [Int], probs: [Float]) {
76 | let x = Array(arr.enumerated().map { ($0, $1) }
77 | .sorted(by: { a, b -> Bool in a.1 > b.1 })
78 | .prefix(through: min(k, arr.count) - 1))
79 | let indexes = x.map { $0.0 }
80 | let logits = x.map { Float($0.1) }
81 | let probs = softmax(logits)
82 | return (indexes: indexes, probs: probs)
83 | }
84 |
85 | /// Multinomial sampling from an array of probs. Works well with topK
86 | static func sample(indexes: [Int], probs: [Float]) -> Int {
87 | let i = randomNumber(probabilities: probs)
88 | return indexes[i]
89 | }
90 |
91 | /**
92 | Computes the "softmax" function over an array.
93 | Based on code from https://github.com/nikolaypavlov/MLPNeuralNet/
94 | This is what softmax looks like in "pseudocode" (actually using Python
95 | and numpy):
96 | x -= np.max(x)
97 | exp_scores = np.exp(x)
98 | softmax = exp_scores / np.sum(exp_scores)
99 | First we shift the values of x so that the highest value in the array is 0.
100 | This ensures numerical stability with the exponents, so they don't blow up.
101 | */
102 | static func softmax(_ x: [Float]) -> [Float] {
103 | var x = x
104 | let len = vDSP_Length(x.count)
105 |
106 | // Find the maximum value in the input array.
107 | var max: Float = 0
108 | vDSP_maxv(x, 1, &max, len)
109 |
110 | // Subtract the maximum from all the elements in the array.
111 | // Now the highest value in the array is 0.
112 | max = -max
113 | vDSP_vsadd(x, 1, &max, &x, 1, len)
114 |
115 | // Exponentiate all the elements in the array.
116 | var count = Int32(x.count)
117 | vvexpf(&x, x, &count)
118 |
119 | // Compute the sum of all exponentiated values.
120 | var sum: Float = 0
121 | vDSP_sve(x, 1, &sum, len)
122 |
123 | // Divide each element by the sum. This normalizes the array contents
124 | // so that they all add up to 1.
125 | vDSP_vsdiv(x, 1, &sum, &x, 1, len)
126 |
127 | return x
128 | }
129 |
130 | /// Multinomial sampling
131 | ///
132 | /// From https://stackoverflow.com/questions/30309556/generate-random-numbers-with-a-given-distribution
133 | ///
134 | static func randomNumber(probabilities: [Float]) -> Int {
135 | // Sum of all probabilities (so that we don't have to require that the sum is 1.0):
136 | let sum = probabilities.reduce(0, +)
137 | // Random number in the range 0.0 <= rnd < sum :
138 | let rnd = sum * Float(arc4random_uniform(UInt32.max)) / Float(UInt32.max)
139 | // Find the first interval of accumulated probabilities into which `rnd` falls:
140 | var accum: Float = 0.0
141 | for (i, p) in probabilities.enumerated() {
142 | accum += p
143 | if rnd < accum {
144 | return i
145 | }
146 | }
147 | // This point might be reached due to floating point inaccuracies:
148 | return (probabilities.count - 1)
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/huggingface-ios-app/Sources/GPT2Tokenizer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GPT2Tokenizer.swift
3 | // CoreMLBert
4 | //
5 | // Created by Julien Chaumond on 18/07/2019.
6 | // Copyright © 2019 Hugging Face. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | fileprivate struct BytePair: Hashable {
12 | let a: String
13 | let b: String
14 | init(_ a: String, _ b: String) {
15 | self.a = a
16 | self.b = b
17 | }
18 | init(tuple: [String]) {
19 | self.a = tuple[0]
20 | self.b = tuple[1]
21 | }
22 |
23 | static func == (lhs: BytePair, rhs: BytePair) -> Bool {
24 | return lhs.a == rhs.a && lhs.b == rhs.b
25 | }
26 | func hash(into hasher: inout Hasher) {
27 | hasher.combine(a)
28 | hasher.combine(b)
29 | }
30 | }
31 |
32 | fileprivate extension String {
33 | func ranges(of string: String, options: CompareOptions = .regularExpression) -> [Range] {
34 | var result: [Range] = []
35 | var start = startIndex
36 | while let range = range(of: string, options: options, range: start..
49 | private let encoder: [String: Int]
50 | private let decoder: [Int: String]
51 |
52 | init(urlMerges: URL, urlVocab: URL ) throws {
53 | let bpeMergesTxt = try! String(contentsOf: urlMerges)
54 | let arr = bpeMergesTxt.split(separator: "\n").map { String($0) }
55 | var bpeRanks: Dictionary = [:]
56 | for i in 1.. GPT2Tokenizer {
73 | guard let urlMerges = Bundle.main.url(forResource: model_name + "/merges", withExtension: "txt"),
74 | let urlVocab = Bundle.main.url(forResource: model_name + "/vocab", withExtension: "json") else {
75 | throw TokenizerError.invalidModelName(model_name)
76 | }
77 |
78 | return try GPT2Tokenizer(urlMerges: urlMerges, urlVocab: urlVocab)
79 | }
80 |
81 | func byteEncode(text: String) -> [String] {
82 | let RE = #"'s|'t|'re|'ve|'m|'ll|'d| ?\p{L}+| ?\p{N}+| ?[^\s\p{L}\p{N}]+|\s+(?!\S)|\s+"#
83 | let tokens = text.ranges(of: RE).map { String(text[$0]) }
84 | return tokens.map { (token) -> String in
85 | return Array(token.utf8).map { byteEncoder[$0]! }.joined()
86 | }
87 | }
88 |
89 | private func getPairs(word: [String]) -> Set {
90 | var s = Set()
91 | for i in 0.. String {
102 | if token.count <= 1 {
103 | return token
104 | }
105 |
106 | var word = Array(token).map { String($0) }
107 | var pairs = Array(getPairs(word: word))
108 |
109 | while true {
110 | let bigrams = pairs.filter { (bp) -> Bool in bpeRanks[bp] != nil }
111 | if bigrams.count == 0 {
112 | break
113 | }
114 | let bigram = bigrams.min { (bp1, bp2) -> Bool in
115 | return bpeRanks[bp1]! < bpeRanks[bp2]!
116 | }!
117 | let first = bigram.a
118 | let second = bigram.b
119 | var newWord: [String] = []
120 | var i = 0
121 | while i < word.count {
122 | if let j = word[i.. [String] {
149 | var tokens: [String] = []
150 | for token in self.byteEncode(text: text) {
151 | let xx = self.bpe(token: token).split(separator: " ").map { String($0) }
152 | tokens.append(contentsOf: xx)
153 | }
154 | return tokens
155 | }
156 |
157 | /// Main entry point
158 | func encode(text: String) -> [Int] {
159 | return tokenize(text: text).map { encoder[$0]! }
160 | }
161 |
162 | /// Decode
163 | func decode(tokens: [Int]) -> String {
164 | let text = tokens.map { decoder[$0] ?? "X" }.joined(separator: "")
165 | let utfCodepoints = text.map { byteDecoder[String($0)]! }
166 | return String(decoding: utfCodepoints, as: UTF8.self)
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/huggingface-ios-app/Sources/BartTokenizer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BartTokenizer.swift
3 | // huggingface-ios-app
4 | //
5 | // Created by Daniel Vela on 12/5/23.
6 | //
7 |
8 | import Foundation
9 |
10 | fileprivate struct BytePair: Hashable {
11 | let a: String
12 | let b: String
13 | init(_ a: String, _ b: String) {
14 | self.a = a
15 | self.b = b
16 | }
17 | init(tuple: [String]) {
18 | self.a = tuple[0]
19 | self.b = tuple[1]
20 | }
21 |
22 | static func == (lhs: BytePair, rhs: BytePair) -> Bool {
23 | return lhs.a == rhs.a && lhs.b == rhs.b
24 | }
25 | func hash(into hasher: inout Hasher) {
26 | hasher.combine(a)
27 | hasher.combine(b)
28 | }
29 | }
30 |
31 | fileprivate extension String {
32 | func ranges(of string: String, options: CompareOptions = .regularExpression) -> [Range] {
33 | var result: [Range] = []
34 | var start = startIndex
35 | while let range = range(of: string, options: options, range: start..
45 | private let encoder: [String: Int]
46 | private let decoder: [Int: String]
47 |
48 | init(urlMerges: URL, urlVocab: URL) throws {
49 | let bpeMergesTxt = try String(contentsOf: urlMerges)
50 | let arr = bpeMergesTxt.split(separator: "\n").map { String($0) }
51 | var bpeRanks: Dictionary = [:]
52 | for i in 1.. BartTokenizer {
69 | guard let urlMerges = Bundle.main.url(forResource: model_name + "/merges", withExtension: "txt"),
70 | let urlVocab = Bundle.main.url(forResource: model_name + "/vocab", withExtension: "json") else {
71 | throw TokenizerError.invalidModelName(model_name)
72 | }
73 |
74 | return try BartTokenizer(urlMerges: urlMerges, urlVocab: urlVocab)
75 | }
76 |
77 | func byteEncode(text: String) -> [String] {
78 | let range = NSRange(location: 0, length: text.utf16.count)
79 | let pattern = #"\S+\n?"#
80 | let regex = try! NSRegularExpression(pattern: pattern)
81 | let tokens = regex.matches(in: text, range: range).map { String(text[Range($0.range, in: text)!]) }.map { $0.lowercased() }
82 | return tokens.map { (token) -> String in
83 | return Array(token.utf8).map { byteEncoder[$0]! }.joined()
84 | }
85 | }
86 |
87 | private func getPairs(word: [String]) -> Set {
88 | var s = Set()
89 | for i in 0.. String {
100 | var token = token.replacingOccurrences(of: "([.,!?()])", with: " $1", options: .regularExpression)
101 | token = token.replacingOccurrences(of: "(')", with: " $1 ", options: .regularExpression)
102 | token = token.replacingOccurrences(of: "\\s{2,}", with: " ", options: .regularExpression)
103 | if token.contains("\n") {
104 | token = token.replacingOccurrences(of: "\n", with: " __newln__")
105 | }
106 |
107 | let tokens = token.split(separator: " ")
108 | var words = [String]()
109 | for token in tokens {
110 | guard !token.isEmpty else {
111 | continue
112 | }
113 | var token = token.lowercased()
114 | var word = Array(token).map { String($0) }
115 | word = Array(word[0.."]
116 | var pairs = getPairs(word: word)
117 |
118 | if pairs.isEmpty {
119 | words.append(token)
120 | continue
121 | }
122 |
123 | while true {
124 | let bigrams = pairs.filter { (bp) -> Bool in bpeRanks[bp] != nil }
125 | if bigrams.count == 0 {
126 | break
127 | }
128 | let bigram = bigrams.min { (bp1, bp2) -> Bool in
129 | return bpeRanks[bp1]! < bpeRanks[bp2]!
130 | }!
131 | let first = bigram.a
132 | let second = bigram.b
133 | var newWord = [String]()
134 | var i = 0
135 |
136 | while i < word.count {
137 | if let j = word[i.. [String] {
165 | var tokens: [String] = []
166 | for token in self.byteEncode(text: text) {
167 | let xx = self.bpe(token: token).split(separator: " ").map { String($0) }
168 | tokens.append(contentsOf: xx)
169 | }
170 | return tokens
171 | }
172 |
173 | /// Main entry point
174 | func encode(text: String) -> [Int] {
175 | return tokenize(text: text).map { encoder[$0]! }
176 | }
177 |
178 | /// Decode
179 | func decode(tokens: [Int]) -> String {
180 | let text = tokens.map { decoder[$0]! }.joined(separator: "")
181 | let utfCodepoints = text.map { byteDecoder[String($0)]! }
182 | return String(decoding: utfCodepoints, as: UTF8.self)
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/huggingface-ios-app/Sources/MLMultiArray+Utils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MLMultiArray+Utils.swift
3 | // CoreMLBert
4 | //
5 | // Created by Julien Chaumond on 27/06/2019.
6 | // Copyright © 2019 Hugging Face. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import CoreML
11 |
12 | extension MLMultiArray {
13 | /// All values will be stored in the last dimension of the MLMultiArray (default is dims=1)
14 | static func from(_ arr: [Int], dims: Int = 1) -> MLMultiArray {
15 | var shape = Array(repeating: 1, count: dims)
16 | shape[shape.count - 1] = arr.count
17 | /// Examples:
18 | /// dims=1 : [arr.count]
19 | /// dims=2 : [1, arr.count]
20 | ///
21 | let o = try! MLMultiArray(shape: shape as [NSNumber], dataType: .int32)
22 | let ptr = UnsafeMutablePointer(OpaquePointer(o.dataPointer))
23 | for (i, item) in arr.enumerated() {
24 | ptr[i] = Int32(item)
25 | }
26 | return o
27 | }
28 |
29 | /// This will concatenate all dimensions into one one-dim array.
30 | static func toIntArray(_ o: MLMultiArray) -> [Int] {
31 | var arr = Array(repeating: 0, count: o.count)
32 | let ptr = UnsafeMutablePointer(OpaquePointer(o.dataPointer))
33 | for i in 0.. [Double] {
41 | var arr: [Double] = Array(repeating: 0, count: o.count)
42 | let ptr = UnsafeMutablePointer(OpaquePointer(o.dataPointer))
43 | for i in 0.. [Float32] {
51 | var arr: [Float32] = Array(repeating: 0, count: o.count)
52 | let ptr = UnsafeMutablePointer(OpaquePointer(o.dataPointer))
53 | for i in 0.. MLMultiArray {
71 | let arr = try! MLMultiArray(shape: shape as [NSNumber], dataType: .double)
72 | let ptr = UnsafeMutablePointer(OpaquePointer(arr.dataPointer))
73 | for i in 0.., indexing: [Indexing]) -> MLMultiArray {
93 | let ranges = indexing.enumerated().map { (i, indexing) in
94 | switch indexing {
95 | case .select(let select):
96 | return select..