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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//! # What is smelte-rs ?
//!
//! Smelt is a ML library focusing on inference, small depedencies with as many optimizations
//! as possible, and still be readable and easy to use.
//!
//! Keep unsafe usage limited and only for performance.
//!
//! # Running models
//!
//! Try running Bert on text classification example.
//!
//! ```bash
//! # Download the model + tokenizer + config
//! # This is a clone of https://huggingface.co/ProsusAI/finbert with safetensors support.
//! curl https://huggingface.co/Narsil/finbert/resolve/main/model.safetensors -o model-Narsil-finbert.safetensors -L
//! curl https://huggingface.co/Narsil/finbert/resolve/main/tokenizer.json -o tokenizer-Narsil-finbert.json -L
//! curl https://huggingface.co/Narsil/finbert/resolve/main/config.json -o config-Narsil-finbert.json -L
//!
//! # Linux
//! cargo run --example bert --release --features intel-mkl -- "This is a test" -n 3
//!
//! # M1
//! cargo run --example bert --release -- "This is a test" -n 3
//! ```
//!
//! # Why not use library X ?
//!
//! Many other libraries for ML out there, torch and tensorflow are great but
//! are now extremely heavy with no option to statically link against.
//! Libraries like ONNX are great too, but when an operator is missing out, it's
//! really hard to work against.
//!
//! For low level libraries. [ggml](https://github.com/ggerganov/ggml) is a great
//! library, no dependencies, extremely small binary size. It's actually an
//! inspiration for this project ! But I'm not good enough a C++ programmer to hack it
//! efficiently enough. Also it's hard to use outside of the intended scope, for
//! instance when writing a webserver/API, or if we wanted to use CUDA as a backend.
//!
//! [dfdx](https://github.com/coreylowman/dfdx) is another super nice project.
//! I drew inspiration from it too. The problem with dfdx was the typing system
//! which while extremely powerful (compile time size checking) it was getting
//! in the way of getting things done, and optimizing for it is not as trivial as
//! it's harder to know what's going on.
//!
//! # The architecture of this library:
//!
//! - [cpu] is containing all the various precisions backend operations, tensor structs.
//! This is your go-to if you want to code everything from scratch.
//! - [nn] contains all the basic layers, and actual model implementations. Code should
//! look closely like torch implementations.
//! - [traits] Contains the glue that allows [nn] to be written independantly of [cpu]
//! which should hopefully making using different precisions (or backends) quite easy.
//!
//!
//! # How does the model look like:
//!
//! ```ignore
//! pub struct BertClassifier<T: Tensor + TensorOps<T>> {
//! bert: Bert<T>,
//! pooler: BertPooler<T>,
//! classifier: Linear<T>,
//! }
//!
//! impl<T: Tensor + TensorOps<T>> BertClassifier<T> {
//! pub fn new(bert: Bert<T>, pooler: BertPooler<T>, classifier: Linear<T>) -> Self {
//! Self {
//! bert,
//! pooler,
//! classifier,
//! }
//! }
//! pub fn forward(&self, input_ids: &[usize], type_ids: &[usize]) -> Result<T, SmeltError> {
//! let tensor = self.bert.forward(input_ids, type_ids)?;
//! let tensor = self.pooler.forward(&tensor)?;
//! let mut logits = self.classifier.forward(&tensor)?;
//! T::softmax(&mut logits)?;
//! Ok(logits)
//! }
//! }
//! ```
//!
//! # What's the performance like ?
//!
//! On a relatively old computer (i7-4790 CPU) This gives ~40ms/token for GPT-2
//! in full f32 precision.
//! For comparison, on the same hardware `torch` gives ~47ms/token and ggml ~37ms.
//!
//! Current implementations does *not* use threading, nor precomputed gelu/exp
//! nor f16 shortcuts that ggml can use (like for the softmax).
//!
//! So there is still lots of room for improvement, and most of the current performance
//! comes from using `intel-mkl` library, which can be dropped once this implements
//! the various ops from ggml (hopefully to get the full performance).
/// The various CPU implementations
/// The neural networks
/// The traits for generic implementations
/// Error linked to the tensor creation
/// Potential errors when using the library