View SourceEvision Example - Simple use of Evision in a Machine Learning Pipeline with Nx and torhcx
Mix.install([{:evision,"~> 0.2"},{:req,"~> 0.5"},{:torchx,"~> 0.3"},{:nx,"~> 0.3",override:true},{:kino,"~> 0.7"},{:scidata,"~> 0.1"}],system_env:[# optional, defaults to `true`# set `EVISION_PREFER_PRECOMPILED` to `false`# if you prefer `:evision` to be compiled from source# note that to compile from source, you may need at least 1GB RAM{"EVISION_PREFER_PRECOMPILED",true},# optional, defaults to `true`# set `EVISION_ENABLE_CONTRIB` to `false`# if you don't need modules from `opencv_contrib`{"EVISION_ENABLE_CONTRIB",true},# optional, defaults to `false`# set `EVISION_ENABLE_CUDA` to `true`# if you wish to use CUDA related functions# note that `EVISION_ENABLE_CONTRIB` also has to be `true`# because cuda related modules come from the `opencv_contrib` repo{"EVISION_ENABLE_CUDA",false},# required when# - `EVISION_ENABLE_CUDA` is `true`# - and `EVISION_PREFER_PRECOMPILED` is `true`## set `EVISION_CUDA_VERSION` to the version that matches# your local CUDA runtime version## current available versions are# - 118# - 121{"EVISION_CUDA_VERSION","118"},# require for Windows users when# - `EVISION_ENABLE_CUDA` is `true`# set `EVISION_CUDA_RUNTIME_DIR` to the directory that contains# CUDA runtime libraries{"EVISION_CUDA_RUNTIME_DIR","C:/PATH/TO/CUDA/RUNTIME"}])
:ok
Define Some Helper Functions and Download the Test Image
# change to the file's directory# or somewhere you have write permissionFile.cd!(__DIR__)defmoduleHelperdodefdownload!(url,save_as,overwrite?\\false)dounlessFile.exists?(save_as)doReq.get!(url,http_errors::raise,into:File.stream!(save_as),cache:notoverwrite?)end:okendendHelper.download!("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg","cat.jpg")
:ok
Read the Test Image
# in real-life use cases, the input source might be a camera# instead of downloading a file and reading italiasEvision,as:Cvimg=Cv.imread("cat.jpg",flags:Cv.Constant.cv_IMREAD_ANYCOLOR())resized_img=Cv.resize(img,{128,128})Cv.imencode(".png",resized_img)|>Kino.Image.new(:png)
Select a Default Nx Backend
# by default we don't have the LibTorch backend# but if you listed :torchx as a dependency# then please uncomment the following line to use the LibTorch backend# Similarly for the EXLA backend# Nx.default_backend(Torchx.Backend)
# training code# based on https://github.com/elixir-nx/nx/blob/e4454423f7be39d3adc9dea76526185fbfaf7a58/exla/examples/mnist.exsdefmoduleDenseNNdoimportNx.Defndefninit_random_paramsdo# 3 layers# 1. Dense(32) with sigmoid# 2. Dense(24) with sigmoid# 3. Dense(10) with softmaxw1=Nx.random_normal({3072,32},0.0,0.1,names:[:input,:layer1])b1=Nx.random_normal({32},0.0,0.1,names:[:layer1])w2=Nx.random_normal({32,24},0.0,0.1,names:[:layer1,:layer2])b2=Nx.random_normal({24},0.0,0.1,names:[:layer2])w3=Nx.random_normal({24,10},0.0,0.1,names:[:layer2,:output])b3=Nx.random_normal({10},0.0,0.1,names:[:output]){w1,b1,w2,b2,w3,b3}enddefnsoftmax(logits)doNx.exp(logits)/Nx.sum(Nx.exp(logits),axes:[:output],keep_axes:true)enddefnpredict({w1,b1,w2,b2,w3,b3},batch)dobatch|>Nx.dot(w1)|>Nx.add(b1)|>Nx.sigmoid()|>Nx.dot(w2)|>Nx.add(b2)|>Nx.sigmoid()|>Nx.dot(w3)|>Nx.add(b3)|>softmax()enddefnaccuracy({w1,b1,w2,b2,w3,b3},batch_images,batch_labels)doNx.mean(Nx.equal(Nx.argmax(batch_labels,axis::output),Nx.argmax(predict({w1,b1,w2,b2,w3,b3},batch_images),axis::output))|>Nx.as_type({:s,8}))enddefnloss({w1,b1,w2,b2,w3,b3},batch_images,batch_labels)dopreds=predict({w1,b1,w2,b2,w3,b3},batch_images)-Nx.sum(Nx.mean(Nx.log(preds)*batch_labels,axes:[:output]))enddefnupdate({w1,b1,w2,b2,w3,b3}=params,batch_images,batch_labels,step)do{grad_w1,grad_b1,grad_w2,grad_b2,grad_w3,grad_b3}=grad(params,&loss(&1,batch_images,batch_labels)){w1-grad_w1*step,b1-grad_b1*step,w2-grad_w2*step,b2-grad_b2*step,w3-grad_w3*step,b3-grad_b3*step}enddefnupdate_with_averages({_,_,_,_,_,_}=cur_params,imgs,tar,avg_loss,avg_accuracy,total)dobatch_loss=loss(cur_params,imgs,tar)batch_accuracy=accuracy(cur_params,imgs,tar)avg_loss=avg_loss+batch_loss/totalavg_accuracy=avg_accuracy+batch_accuracy/total{update(cur_params,imgs,tar,0.01),avg_loss,avg_accuracy}enddeftrain_epoch(cur_params,x,labels)dototal_batches=Enum.count(x)x|>Enum.zip(labels)|>Enum.reduce({cur_params,Nx.tensor(0.0),Nx.tensor(0.0)},fn{x,tar},{cur_params,avg_loss,avg_accuracy}->update_with_averages(cur_params,x,tar,avg_loss,avg_accuracy,total_batches)end)enddeftrain(x,labels,params,opts\\[])doepochs=opts[:epochs]||5forepoch<-1..epochs,reduce:paramsdocur_params->{time,{new_params,epoch_avg_loss,epoch_avg_acc}}=:timer.tc(__MODULE__,:train_epoch,[cur_params,x,labels])epoch_avg_loss=epoch_avg_loss|>Nx.backend_transfer()|>Nx.to_number()epoch_avg_acc=epoch_avg_acc|>Nx.backend_transfer()|>Nx.to_number()IO.puts("Epoch #{epoch} Time: #{time/1_000_000}s, loss: #{Float.round(epoch_avg_loss,3)}, acc: #{Float.round(epoch_avg_acc,3)}")new_paramsendendend