~/bend-docscommunity

tensor_model.bend source

tensor_model.bend on the hub · documented module

# Models: a step computes with tensors it takes by name from a store, so a# network reads as one step after another and its weights are loaded once.import Baseimport ./tensor.bend as Tensorsimport ./tensor_layout.bend as Layoutimport ./tensor_file.bend as Filesdef Tensor() -> Type:  Tensors.Tensor<F32>type Store is Type:  Empty{}  Entry{name: String,tensor: Tensor(),rest: Store}# A step: given the store, a value and the store that remains.def Model(A: Type) -> Type:  Store -> A & Storedef Model.pure(-A: Type,value: A) -> Model(A):  store => (value,store)def Model.next(-A: Type,-B: Type,pair: A & Store,next: A -> Model(B)) -> B & Store:  (value,store) = pair  next(value)(store)def Model.bind(-A: Type,-B: Type,step: Model(A),next: A -> Model(B)) -> Model(B):  store => Model.next(A,B,step(store),next)def missing(+name: String) -> Tensor():  Tensors.failed(~F32,"take",Tensors.MissingName{name},[],0.0,1)def passed(name: String,tensor: Tensor(),pair: Tensor() & Store) -> Tensor() & Store:  (result,store) = pair  (result,Entry{name,tensor,store})def scan(rest: Store,same: Bool,+name: String,tensor: Tensor(),+wanted: String) -> Tensor() & Store:  match rest same:    case Empty{} True{}: (tensor,Empty{})    case Entry{next,other,more} True{}: (tensor,Entry{next,other,more})    case Empty{} False{}: (missing(wanted),Entry{name,tensor,Empty{}})    case Entry{+next,other,more} False{}: passed(name,tensor,scan(more,String.eq(next,wanted),next,other,wanted))def taken(store: Store,+wanted: String) -> Tensor() & Store:  match store:    case Empty{}: (missing(wanted),Empty{})    case Entry{+name,tensor,rest}: scan(rest,String.eq(name,wanted),name,tensor,wanted)# The tensor of that name leaves the store. A name that is not there gives a# failed tensor.def take(+name: String) -> Model(Tensor()):  store => taken(store,name)def kept(+name: String,pair: Tensor() & Tensor(),store: Store) -> Tensor() & Store:  (tensor,copy) = pair  (tensor,Entry{name,copy,store})# Puts a copy under the name, for a later take, and gives the tensor back.def keep(+name: String,tensor: Tensor()) -> Model(Tensor()):  store => kept(name,Layout.copy(~F32,tensor),store)def run(-A: Type,step: Model(A),store: Store) -> A & Store:  step(store)def value(-A: Type,pair: A & Store) -> A:  (result,store) = pair  result# Runs the steps on the store and gives the result; what remains in the store is dropped.def result(-A: Type,step: Model(A),store: Store) -> A:  value(A,step(store))def joined(+name: String,first: Tensor(),second: Tensor(),rest: Store & Store) -> Store & Store:  (left,right) = rest  (Entry{name,first,left},Entry{name,second,right})def copied_entry(+name: String,pair: Tensor() & Tensor(),rest: Store & Store) -> Store & Store:  (first,second) = pair  joined(name,first,second,rest)# Two stores with the same tensors, to run a model more than once.def copy(store: Store) -> Store & Store:  match store:    case Empty{}: (Empty{},Empty{})    case Entry{+name,tensor,rest}: copied_entry(name,Layout.copy(~F32,tensor),copy(rest))# Loading: folder/names.txt lists one name per line; folder/<name>.npy holds its tensor.def entry(+name: String,tensor: Tensor(),store: Store) -> Store:  Entry{name,tensor,store}def load_each(names: List<&2,String>,+folder: String) -> IO(Store):  match names:    case Nil{}: IO.pure(Store,Empty{})    case Con{+name,rest}:      match name:        case SNil{}: load_each(rest,folder)        case SCon{head,tail}:          do IO<Store>:            tensor : Tensor() <- Files.load_npy(folder ++ "/" ++ name ++ ".npy")            store : Store <- load_each(rest,folder)            IO.pure(Store,entry(name,tensor,store))def named(text: Result<&1,&1,U32 & String,String>,+folder: String) -> IO(Store):  match text:    case Fail{error}: IO.pure(Store,Empty{})    case Done{text}: load_each(String.lines(text),folder)def listed(read: File & Result<&1,&1,U32 & String,String>,+folder: String) -> IO(Store):  (file,text) = read  do IO<Store>:    File.close(file)    named(text,folder)def opened(result: Result<&1,&1,U32 & String,File>,+folder: String) -> IO(Store):  match result:    case Fail{error}: IO.pure(Store,Empty{})    case Done{file}:      do IO<Store>:        read : File & Result<&1,&1,U32 & String,String> <- File.read(file,1048576)        listed(read,folder)# A folder without names.txt gives an empty store; every take then fails by name.def load(+folder: String) -> IO(Store):  do IO<Store>:    result : Result<&1,&1,U32 & String,File> <- File.open(folder ++ "/names.txt","r")    opened(result,folder)# Saving: the tensors of a store as folder/<name>.npy, and their names in folder/names.txt.def named_rest(+name: String,tensor: Tensor(),pair: Store & String) -> Store & String:  (rest,text) = pair  (Entry{name,tensor,rest},name ++ "" ++ text)def first(written: File & Result<&1,&1,U32 & String,Unit>) -> File:  (file,result) = written  filedef names(store: Store) -> Store & String:  match store:    case Empty{}: (Empty{},"")    case Entry{+name,tensor,rest}: named_rest(name,tensor,names(rest))def save_each(store: Store,+folder: String) -> IO(Store):  match store:    case Empty{}: IO.pure(Store,Empty{})    case Entry{+name,tensor,rest}:      do IO<Store>:        saved : Tensor() <- Files.save_npy(folder ++ "/" ++ name ++ ".npy",tensor)        others : Store <- save_each(rest,folder)        IO.pure(Store,entry(name,saved,others))def listing(result: Result<&1,&1,U32 & String,File>,text: String) -> IO(Unit):  match result:    case Fail{error}: IO.pure(Unit,Unit{})    case Done{file}:      do IO<Unit>:        written : File & Result<&1,&1,U32 & String,Unit> <- File.write(file,text)        File.close(first(written))def save_listed(pair: Store & String,+folder: String) -> IO(Store):  (store,text) = pair  do IO<Store>:    result : Result<&1,&1,U32 & String,File> <- File.open(folder ++ "/names.txt","w")    listing(result,text)    save_each(store,folder)# Gives the store back; a tensor that could not be written comes back failed.def save(+folder: String,store: Store) -> IO(Store):  save_listed(names(store),folder)