As a data scientist, I feel that Nim has tremendous potential for data science, machine learning and deep learning.
In particular, it's currently non-trivial to bridge the gap between deep learning research (mostly Python and sometimes Lua) and production (C for embedded devices, javascript for web services ...).
For the past 3 months I've been working on Arraymancer, a tensor library that currently provides a subset of Numpy functionality in a fast and ergonomic library. It features:
Next steps (in no particular order) include:
The library: https://github.com/mratsim/Arraymancer
I welcome your feedback or expected use case. I especially would love to know the pain points people have with deep learning and putting deep learning models in production.
I've been following this for a while on GitHub and I think it is a very impressive project. Nim would be a great language for scientific computing, but it needs to have the numerical libraries and this is an excellent first step in creating them.
A couple of questions. First, are you planning to add neural network functionality directly to Arraymancer? Surely that would be something better suited for a separate, specialised library? A second, more general, question I have is whether you'd consider making the get_data_ptr proc public. It would be nice to be able to integrate your tensors with wrappers for existing numerical software written in C and we'd need access to the raw data for that.
get_data_ptr is now public ;).
For now, I will add the neural network functionality directly in Arraymancer.
The directory structure will probably be:
This mirrors PyTorch's tree
I made this choice for the following reasons:
If the tensor part (without the NN) get even 0.1% of Numpy popularity and people start using it in several packages that means:
In the mean time I think it's best if I do what is easier for me and worry about how to scale later.
A late reply because I was hoping to dive into this a bit deeper before replying. But due to lack of time, a high-level feedback must suffice: This looks awesome!
I completely agree with your observation that there is a gap between developing prototypes e.g. in Python and bringing them into production -- not only in deep learning, but data science in general. And I also think that Nim's feature set would be perfect to fill this gap.
A quick question on using statically-typed tensors: I assume that this implies that the topolgy of a network cannot be dynamic at all? I'm wondering if there are good work-arounds to situations where dynamic network topologies are required, for instance when a model wants to choose its number of hidden layer nodes iteratively, picking the best model variant. Are dynamically typed tensors an option or would that defeat the design / performance?
The only static parts of the Tensor types are the Backend (Cpu, CUDA, ...) and the internal type (int32, float32, object ...).
The network topology will be dynamic and using dynamic graphs more akin to PyTorch/Chainer/DyNet than Theano/Tensorflow/Keras.
My next step is to build an autograd so people only need to implement the forward pass, backpropagation will be automatic. For this part I'm waiting for VTable.
PS: I think NimData is great too, Pandas seems like a much harder beast!
I am very excited to announce the second release of Arraymancer which includes numerous improvements blablabla ...
Without further ado:
those out you can load images in Tensor and do logistic regression on those!
Also thanks to the Nim communauty on IRC/Gitter, they are a tremendous help (yes Varriount, Yardanico, Zachary, Krux).
I probably would have struggled a lot more without the guidance of Andrea's code for Cuda in his neo and nimcuda library.
And obviously Araq and Dom for Nim which is an amazing language for performance, productivity, safety and metaprogramming.
Arraymancer v0.3.0 Dec. 14 2017
Finally after much struggles, here is Arraymancer new version. Available now on Nimble. It comes with a new shiny doc (thanks @flyx and NimYAML doc): https://mratsim.github.io/Arraymancer
Changes:
- Tensors uses reference semantics now: let a = b will share data by default and copies must be made explicitly.
- There is no need to use unsafe proc to avoid copies especially for slices.
- Unsafe procs are deprecated and will be removed leading to a smaller and simpler codebase and API/documentation.
- Tensors and CudaTensors now works the same way.
- Use clone to do copies.
- Arraymancer now works like Numpy and Julia, making it easier to port code.
- Unfortunately it makes it harder to debug unexpected data sharing.
- The max number of dimensions supported has been reduced from 8 to 7 to reduce cache misses. Note, in deep learning the max number of dimensions needed is 6 for 3D videos: [batch, time, color/feature channels, Depth, Height, Width]
- Documentation has been completely revamped and is available here: https://mratsim.github.io/Arraymancer/
- Use non-initialized seq
- shape and strides are now stored on the stack
- optimization via inlining all higher-order functions
- apply_inline, map_inline, fold_inline and reduce_inline templates are available.
- all higher order functions are parallelized through OpenMP
- integer matrix multiplication uses SIMD, loop unrolling, restrict and 64-bit alignment
- prevent false sharing/cache contention in OpenMP reduction
- remove temporary copies in several proc
- runtime checks/exception are now behind unlikely
- A*B + C and C+=A*B are automatically fused in one operation
- do not initialize result tensors
- Added linear, sigmoid_cross_entropy,
- softmax_cross_entropy layers
- Added Convolution layer
- Added unsqueeze and stack
- Added min, max, abs, reciprocal, negate and in-place mnegate and mreciprocal
- Added variance and standard deviation
- Added .^ (broadcasted exponentiation)
- Support for convolution primitives: forward and backward
- Broadcasting ported to Cuda
- Added perceptron learning xor function example
- Arraymancer uses ln1p (ln(1 + x)) and exp1m procs (exp(1 - x)) where appropriate to avoid catastrophic cancellation
- Version 0.3.1 with the ALL deprecated proc removed will be released in a week. Due to issue https://github.com/nim-lang/Nim/issues/6436, even using non-deprecated proc like zeros, ones, newTensor you will get a deprecated warning.
- newTensor, zeros, ones arguments have been changed from zeros([5, 5], int) to zeros[int]([5, 5])
- All unsafe proc are now default and deprecated.
Several updates linked to Nim rapid development and several bugfixes.
Congratulations !
I especially like the neural network examples and hope more
will be forthcoming.
There is a memowner bool in the type. If it's false, it will not deallocate memory when not referenced.
const LASER_MAXRANK*{.intdefine.} = 6
type DynamicStackArray*[T] = object
data*: array[LASER_MAXRANK, T]
len*: int
type
RawImmutableView*[T] = distinct ptr UncheckedArray[T]
RawMutableView*[T] = distinct ptr UncheckedArray[T]
Metadata* = DynamicStackArray[int]
Tensor*[T] = object # Total stack: 128 bytes = 2 cache-lines
shape*: Metadata # 56 bytes
strides*: Metadata # 56 bytes
offset*: int # 8 bytes
storage*: CpuStorage[T] # 8 bytes
CpuStorage*{.shallow.}[T] = ref object # Total heap: 25 bytes = 1 cache-line
when supportsCopyMem(T):
raw_data*: ptr UncheckedArray[T] # 8 bytes
memalloc*: pointer # 8 bytes
memowner*: bool # 1 byte
else: # Tensors of strings, other ref types or non-trivial destructors
raw_data*: seq[T] # 8 bytes (16 for seq v2 backed by destructors?)
I've released a new stable version of Arraymancer v0.5.1.
The number of changes is small in number but great in quality:
https://github.com/mratsim/Arraymancer/releases/tag/v0.5.1
Changes affecting backward compatibility:
Changes:
Einsum / einstein summation: This allows using C|i, j] = A[i,k] * B[k,j] to specify
for i in 0 ..< A.shape[0]:
for k in 0 ..< A.shape[1]:
for j in 0 ..< B.shape[1]:
C[i,j] += A[i,k] * B[k,j]
Fix:
Thanks to @chimez for the complex support and updating for 0.20, @metasyn for the tokenizer, @xcokazaki for the image dimension fix and @Vindaar for the einsum implemention
Very impressive matmul results in Kostya's benchmarks.
Would be even better if s/Apache/MIT/ license...
I didn't even know it was in Kostya, but technically just like Julia, Numpy, and Lubeck (D) the current version of Arraymancer is just using BLAS behind (which are 95% C)
That said I do get similar result in a pure Nim BLAS, with both OpenMP-based or with Weave-based threading:
Regarding license, point taken. Most of my new libs are Status-Style, dual licensed Apache+MIT:
Yes I'm very familiar with BLIS and my own BLAS is implemented following the BLIS paper http://www.cs.utexas.edu/users/flame/pubs/blis3_ipdps14.pdf
Actually some BLIS developers are also aware of my own efforts: