Base module¶
Classes¶
- 
class k1lib.Learner[source]¶
- Bases: - object- 
property model¶
- Set this to change the model to run 
 - 
property data¶
- Set this to change the data (list of 2 dataloader) to run against. 
 - 
property opt¶
- Set this to change the optimizer. If you’re making your own optimizers, beware to follow the PyTorch’s style guide as there are callbacks that modifies optimizer internals while training like - k1lib.schedule.ParamScheduler.
 - 
property cbs¶
- The - Callbacksobject. Initialized to include all the common callbacks. You can set a new one if you want to.
 - 
property css¶
- The css selector string. Set this to select other parts of the network. After setting, you can access the selector like this: - l.selector- See also: - ModuleSelector
 - 
property lossF¶
- Set this to specify a loss function. 
 - 
evaluate()[source]¶
- Function to visualize quickly how the network is doing. Undefined by default, just placed here as a convention, so you have to do something like this: - l = k1lib.Learner() def evaluate(self): xbs, ybs, ys = self.Recorder.record(1, 3) plt.plot(torch.vstack(xbs), torch.vstack(ys)) l.evaluate = partial(evaluate(l)) 
 - 
__call__(xb, yb=None)¶
- Executes just a small batch. Convenience method to query how the network is doing. - Parameters
- xb – x batch 
- yb – y batch. If specified, return (y, loss), else return y alone 
 
 
 - 
static load(fileName: Optional[str] = None)¶
- Loads a - Learnerfrom a file. See also:- save()- Parameters
- fileName – if empty, then will prompt for file name 
 
 - 
run(epochs: int, batches: Optional[int] = None)¶
- Main run function. - Parameters
- epochs – number of epochs to run. 1 epoch is the length of the dataset 
- batches – if set, then cancels the epoch after reaching the specified batch 
 
 
 - 
static sample() → k1lib._learner.Learner¶
- Creates an example learner, just for simple testing stuff anywhere. The network tries to learn the function y=x. Only bare minimum callbacks are included. 
 
- 
property 
- 
class k1lib.Object[source]¶
- Bases: - object- Convenience class that acts like - defaultdict. You can use it like a normal object:- a = Object() a.b = 3 print(a.b) # outputs "3" - __repr__()output is pretty nice too:- <class '__main__.Object'>, with attrs: - b - You can instantiate it from a dict: - a = Object.fromDict({"b": 3, "c": 4}) print(a.c) # outputs "4" - And you can specify a default value, just like defaultdict: - a = Object().withAutoDeclare(lambda: []) a.texts.extend(["factorio", "world of warcraft"]) print(a.texts[0]) # outputs "factorio" - Warning - Default values only work with variables that don’t start with an underscore “_”. - Treating it like defaultdict is okay too: - a = Object().withAutoDeclare(lambda: []) a["movies"].append("dune") print(a.movies[0]) # outputs "dune" - 
property state¶
- Essentially - __dict__, but only outputs the fields you defined. If your framework intentionally set some attributes, those will be reported too, so beware
 
- 
property 
- 
class k1lib.Range(start=0, stop=None)[source]¶
- Bases: - object- A range of numbers. It’s just 2 numbers really: start and stop - This is essentially a convenience class to provide a nice, clean abstraction and to eliminate errors. You can transform values: - Range(10, 20).toUnit(13) # returns 0.3 Range(10, 20).fromUnit(0.3) # returns 13 Range(10, 20).toRange(Range(20, 10), 13) # returns 17 - You can also do random math operations on it: - (Range(10, 20) * 2 + 3) == Range(23, 43) # returns True Range(10, 20) == ~Range(20, 10) # returns True - 
__getitem__(index)[source]¶
- 0 for start, 1 for stop - You can also pass in a - sliceobject, in which case, a range subset will be returned. Code kinda looks like this:- range(start, stop)[index] 
 - 
__init__(start=0, stop=None)[source]¶
- Creates a new Range. - There are different - __init__functions for many situations:- Range(2, 11.1): create range [2, 11.1] 
- Range(15.2): creates range [0, 15.2] 
- Range(Range(2, 3)): create range [2, 3]. This serves as sort of a catch-all 
- Range(slice(2, 5, 2)): creates range [2, 5]. Can also be a - range
- Range(slice(2, -1), 10): creates range [2, 9] 
- Range([1, 2, 7, 5]): creates range [1, 5]. Can also be a tuple 
 
 - 
fixOrder() → k1lib._baseClasses.Range[source]¶
- If start greater than stop, switch the 2, else do nothing 
 - 
toUnit(x)[source]¶
- Converts x from current range to [0, 1] range. Example: - r = Range(2, 10) r.toUnit(5) # will return 0.375, as that is (5-2)/(10-2) - You can actually pass in a lot in place of x: - r = Range(0, 10) r.toUnit([5, 3, 6]) # will be [0.5, 0.3, 0.6]. Can also be a tuple r.toUnit(slice(5, 6)) # will be slice(0.5, 0.6). Can also be a range, or Range - Note - In the last case, if - startis None, it gets defaulted to 0, and if- endis None, it gets defaulted to 1
 - 
fromUnit(x)[source]¶
- Converts x from [0, 1] range to this range. Example: - r = Range(0, 10) r.fromUnit(0.3) # will return 3 - x can be a lot of things, see - toUnit()for more
 - 
toRange(_range: k1lib._baseClasses.Range, x)[source]¶
- Converts x from current range to another range. Example: - r = Range(0, 10) r.toRange(Range(0, 100), 6) # will return 60 - x can be a lot of things, see - toUnit()for more.
 - 
static proportionalSlice(r1, r2, r1Slice: slice) → Tuple[k1lib._baseClasses.Range, k1lib._baseClasses.Range][source]¶
- Slices r1 and r2 proportionally. Best to explain using an example. Let’s say you have 2 arrays created from a time-dependent procedure like this: - a = []; b = [] for t in range(100): if t % 3 == 0: a.append(t) if t % 5 == 0: b.append(1 - t) len(a), len(b) # returns (34, 20) - a and b are of different lengths, but you want to plot both from 30% mark to 50% mark (for a, it’s elements 10 -> 17, for b it’s 6 -> 10), as they are time-dependent. As you can probably tell, to get the indicies 10, 17, 6, 10 is messy. So, you can do something like this instead: - r1, r2 = Range.proportionalSlice(Range(len(a)), Range(len(b)), slice(10, 17)) - This will return the Ranges [10, 17] and [5.88, 10] - Then, you can plot both of them side by side like this: - fig, axes = plt.subplots(ncols=2) axes[0].plot(r1.range_, a[r1.slice_]) axes[1].plot(r2.range_, a[r2.slice_]) 
 
- 
- 
class k1lib.Domain(*ranges, dontCheck: bool = False)[source]¶
- Bases: - object- 
__init__(*ranges, dontCheck: bool = False)[source]¶
- Creates a new domain. - Parameters
- ranges – each element is a - Range, although any format will be fine as this selects for that
- dontCheck – don’t sanitize inputs, intended to boost perf internally only 
 
 - A domain is just an array of - Rangethat represents what intervals on the real number line is chosen. Some examples:- inf = float("inf") # shorthand for infinity Domain([5, 7.5], [2, 3]) # represents "[2, 3) U [5, 7.5)" Domain([2, 3.2], [3, 8]) # represents "[2, 8)" as overlaps are merged -Domain([2, 3]) # represents "(-inf, 2) U [3, inf)", so essentially R - d, with R being the set of real numbers -Domain([-inf, 3]) # represents "[3, inf)" Domain.fromInts(2, 3, 6) # represents "[2, 4) U [6, 7)" - You can also do arithmetic on them, and check “in” oeprator: - Domain([2, 3]) + Domain([4, 5]) # represents "[2, 3) U [4, 5)" Domain([2, 3]) + Domain([2.9, 5]) # represents "[2, 5)", also merges overlaps 3 in Domain([2, 3]) # returns False 2 in Domain([2, 3]) # returns True 
 
- 
- 
class k1lib.AutoIncrement(initialValue: int = - 1, n: int = inf)[source]¶
- Bases: - object- 
__init__(initialValue: int = - 1, n: int = inf)[source]¶
- Creates a new AutoIncrement object. Every time the object is called it gets incremented by 1 automatically. Example: - a = k1lib.AutoIncrement() a() # returns 0 a() # returns 1 a() # returns 2 a.value # returns 2 a.value # returns 2 a() # returns 3 a = AutoIncrement(n=3) a() # returns 0 a() # returns 1 a() # returns 2 a() # returns 0 - Parameters
- n – if specified, then will wrap around to 0 when hit this number. 
 
 - 
static random() → k1lib._baseClasses.AutoIncrement[source]¶
- Creates a new AutoIncrement object that has a random integer initial value 
 - 
property value¶
- Get the value as-is, without auto incrementing it 
 
- 
- 
class k1lib.Every(n)[source]¶
- Bases: - object- 
__init__(n)[source]¶
- Returns True every interval. Example: - e = Every(4) e() # returns True e() # returns False e() # returns False e() # returns False e() # returns True 
 - 
property value¶
 
- 
- 
class k1lib.Absorber(initDict: dict = {})[source]¶
- Bases: - object- Creates an object that absorbes every operation done on it. Could be useful in some scenarios: - ab = k1lib.Absorber() # absorbs all operations done on the object abs(ab[::3].sum(dim=1)) t = torch.randn(5, 3, 3) # returns transformed tensor of size [2, 3] ab.ab_operate(t) - Another: - ab = Absorber() ab[2] = -50 # returns [0, 1, -50, 3, 4] ab.ab_operate(list(range(5))) - Because this object absorbs every operation done on it, you have to be gentle with it, as any unplanned disturbances might throw your code off. Best to create a new one on the fly, and pass them immediately to functions, because if you’re in a notebook environment like Jupyter, it might poke at variables. - For extended code example that utilizes this, check over - k1lib.cli.modifier.opsource code.- 
__init__(initDict: dict = {})[source]¶
- Creates a new Absorber. - Parameters
- initDict – initial variables to set, as setattr operation is normally absorbed 
 
 
- 
- 
class k1lib.CaptureStdout(iterable=(), /)[source]¶
- Bases: - list- Captures every print() statement. Taken from https://stackoverflow.com/questions/16571150/how-to-capture-stdout-output-from-a-python-function-call. Example: - with k1lib.CaptureStdout() as outer: print("something") with k1lib.CaptureStdout() as inner: print("inside inner") print("else") # prints "['something', 'else']" print(outer) # prints "['inside inner']" print(inner) - Note that internally, this replaces - sys.stdoutas- io.StringIO, so might not work property if you have fancy- bytesstuff going on.
- 
class k1lib.ignoreWarnings[source]¶
- Bases: - object- Context manager to ignore every warning. Example: - import warnings with k1lib.ignoreWarnings(): warnings.warn("some random stuff") # will not show anything 
- 
class k1lib.wrapMod(m, moduleName=None)[source]¶
- Bases: - object- 
__init__(m, moduleName=None)[source]¶
- Wraps around a module, and only suggest symbols in __all__ list defined inside the module. Example: - from . import randomModule randomModule = wrapMod(randomModule) - Parameters
- m – the imported module 
- moduleName – optional new module name for elements (their __module__ attr) 
 
 
 
- 
Exceptions¶
Functions¶
- 
k1lib.textToHtml(text: str) → str[source]¶
- Transform a string so that it looks the same on browsers as in print() 
- 
k1lib.tab(text: Union[list, str]) → Union[list, str][source]¶
- Adds a tab before each line. str in, str out. List in, list out 
- 
k1lib.patch(_class: type, name: Optional[str] = None, docs: Optional[Union[str, Any]] = None, static=False)[source]¶
- Patches a function to a class/object. - Parameters
- _class – object to patch function. Can also be a type 
- name – name of patched function, if different from current 
- docs – docs of patched function. Can be object with defined __doc__ attr 
- static – whether to wrap this inside - staticmethodor not
 
- Returns
- modified function just before patching 
 - Intended to be used like this: - class A: def methA(self): return "inside methA" @k1lib.patch(A) def methB(self): return "inside methB" a = A() a.methB() # returns "inside methB" - You can do - @propertyattributes like this:- class A: pass @k1lib.patch(A, "propC") @property def propC(self): return self._propC @k1lib.patch(A, "propC") @propC.setter def propC(self, value): self._propC = value a = A(); a.propC = "abc" a.propC # returns "abc" - The attribute name unfortunately has to be explicitly declared, as I can’t really find a way to extract the original name. You can also do static methods like this: - class A: pass @k1lib.patch(A, static=True) def staticD(arg1): return arg1 A.staticD("def") # returns "def" 
- 
k1lib.squeeze(_list: Union[list, tuple, torch.Tensor, Any], hard=False)[source]¶
- If list only has 1 element, returns that element, else returns original list. - Parameters
- hard – If True, then if list/tuple, filters out None, and takes the first element out even if that list/tuple has more than 1 element 
 
- 
k1lib.smooth(arr: List[float], consecutives: int = 5) → List[float][source]¶
- Smoothes out array, so that y values are averages of the neighbors 
- 
k1lib.limitLines(s: str, limit: int = 10) → str[source]¶
- If input string is too long, truncates it and adds ellipsis 
- 
k1lib.limitChars(s: str, limit: int = 50)[source]¶
- If input string is too long, truncates to first limit characters of the first line 
- 
k1lib.showLog(loggerName: str = '', level: int = 10)[source]¶
- Prints out logs of a particular logger at a particular level 
- 
k1lib.executeNb(fileName: str, _globals: Optional[dict] = None, preserveDir=False)[source]¶
- Executes a specified IPython notebook. Can make all variables defined in the notebook appear in the __main__ context by passing globals() in - Parameters
- preserveDir – if True, don’t change working directory to that of the notebook’s 
 
- 
k1lib.dontWrap()[source]¶
- Don’t wrap horizontally when in a notebook. Normally, if you’re displaying something long, like the output of - print('a'*1000)in a notebook, it will display it in multiple lines. This may be undesirable, so this solves that by displaying some HTML with css styles so that the notebook doesn’t wrap.
- 
k1lib.positionalEncode(t: torch.Tensor, richFactor: float = 2) → torch.Tensor[source]¶
- Position encode a tensor of shape \((L, F)\), where \(L\) is the sequence length, \(F\) is the encoded features. Will add the encodings directly to the input tensor and return it. - This is a bit different from the standard implementations that ppl use. This is exactly: \[p = \frac{i}{F\cdot richFactor}\]\[w = 1/10000^p\]\[pe = sin(w * L)\]- With - ifrom range [0, F), and- pthe “progress”. If- richFactoris 1 (original algo), then- pgoes from 0% to 100% of the features. Example:- import matplotlib.pyplot as plt, torch, k1lib plt.figure(dpi=150) plt.imshow(k1lib.positionalEncode(torch.zeros(100, 10)).T)   - Parameters
- richFactor – the bigger, the richer the features are. A lot of times, I observe that the features that are meant to cover huge scales are pretty empty and don’t really contribute anything useful. So this is to bump up the usefulness of those features 
 
- 
k1lib.debounce(wait, threading=False)[source]¶
- Decorator that will postpone a function’s execution until after - waitseconds have elapsed since the last time it was invoked. Taken from ipywidgets. Example:- import k1lib, time; value = 0 @k1lib.debounce(0.5, True) def f(x): global value; value = x**2 f(2); time.sleep(0.3); f(3) print(value) # prints "0" time.sleep(0.7) print(value) # prints "9" - Parameters
- wait – wait time in seconds 
- threading – if True, use multiple threads, else just use async stuff 
 
 
Higher order functions¶
- 
k1lib.polyfit(x: List[float], y: List[float], deg: int = 6) → Callable[[float], float][source]¶
- Returns a function that approximate \(f(x) = y\). - Parameters
- deg – degree of the polynomial of the returned function 
 
- 
k1lib.derivative(f: Callable[[float], float], delta: float = 1e-06) → Callable[[float], float][source]¶
- Returns the derivative of a function. Example: - f = lambda x: x**2 df = k1lib.derivative(f) df(3) # returns roughly 6 
- 
k1lib.optimize(f: Callable[[float], float], v: float = 1, threshold: float = 1e-06) → float[source]¶
- Given \(f(x) = 0\), solves for x using Newton’s method with initial value v. Example: - f = lambda x: x**2-2 # returns 1.4142 (root 2) k1lib.optimize(f) # returns -1.4142 (negative root 2) k1lib.optimize(f, -1) - Interestingly, for some reason, result of this is more accurate than - derivative().
- 
k1lib.inverse(f: Callable[[float], float]) → Callable[[float], float][source]¶
- Returns the inverse of a function. Example: - f = lambda x: x**2 fInv = k1lib.inverse(f) # returns roughly 3 fInv(9) - Warning - The inverse function takes a long time to run, so don’t use this where you need lots of speed. Also, as you might imagine, the inverse function isn’t really airtight. Should work well with monotonic functions, but all bets are off with other functions.