blob: 40a6e845b5e51d9e0902fde567d588d11bb815dd [file] [log] [blame]
<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="lang:clipboard.copy" content="Copy to clipboard">
<meta name="lang:clipboard.copied" content="Copied to clipboard">
<meta name="lang:search.language" content="en">
<meta name="lang:search.pipeline.stopwords" content="True">
<meta name="lang:search.pipeline.trimmer" content="True">
<meta name="lang:search.result.none" content="No matching documents">
<meta name="lang:search.result.one" content="1 matching document">
<meta name="lang:search.result.other" content="# matching documents">
<meta name="lang:search.tokenizer" content="[\s\-]+">
<link rel="shortcut icon" href="../../assets/images/favicon.png">
<meta name="generator" content="mkdocs-1.0.4, mkdocs-material-4.6.0">
<title>Key-Value Store - MXNet.jl</title>
<link rel="stylesheet" href="../../assets/stylesheets/application.1b62728e.css">
<script src="../../assets/javascripts/modernizr.268332fc.js"></script>
<link href="https://fonts.gstatic.com" rel="preconnect" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,400i,700%7CRoboto+Mono&display=fallback">
<style>body,input{font-family:"Roboto","Helvetica Neue",Helvetica,Arial,sans-serif}code,kbd,pre{font-family:"Roboto Mono","Courier New",Courier,monospace}</style>
<link rel="stylesheet" href="../../assets/fonts/material-icons.css">
<link rel="stylesheet" href="../../assets/Documenter.css">
</head>
<body dir="ltr">
<svg class="md-svg">
<defs>
<svg xmlns="http://www.w3.org/2000/svg" width="416" height="448" viewBox="0 0 416 448" id="__github"><path fill="currentColor" d="M160 304q0 10-3.125 20.5t-10.75 19T128 352t-18.125-8.5-10.75-19T96 304t3.125-20.5 10.75-19T128 256t18.125 8.5 10.75 19T160 304zm160 0q0 10-3.125 20.5t-10.75 19T288 352t-18.125-8.5-10.75-19T256 304t3.125-20.5 10.75-19T288 256t18.125 8.5 10.75 19T320 304zm40 0q0-30-17.25-51T296 232q-10.25 0-48.75 5.25Q229.5 240 208 240t-39.25-2.75Q130.75 232 120 232q-29.5 0-46.75 21T56 304q0 22 8 38.375t20.25 25.75 30.5 15 35 7.375 37.25 1.75h42q20.5 0 37.25-1.75t35-7.375 30.5-15 20.25-25.75T360 304zm56-44q0 51.75-15.25 82.75-9.5 19.25-26.375 33.25t-35.25 21.5-42.5 11.875-42.875 5.5T212 416q-19.5 0-35.5-.75t-36.875-3.125-38.125-7.5-34.25-12.875T37 371.5t-21.5-28.75Q0 312 0 260q0-59.25 34-99-6.75-20.5-6.75-42.5 0-29 12.75-54.5 27 0 47.5 9.875t47.25 30.875Q171.5 96 212 96q37 0 70 8 26.25-20.5 46.75-30.25T376 64q12.75 25.5 12.75 54.5 0 21.75-6.75 42 34 40 34 99.5z"/></svg>
</defs>
</svg>
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" data-md-component="overlay" for="__drawer"></label>
<a href="#key-value-store" tabindex="1" class="md-skip">
Skip to content
</a>
<header class="md-header" data-md-component="header">
<nav class="md-header-nav md-grid">
<div class="md-flex">
<div class="md-flex__cell md-flex__cell--shrink">
<a href="../.." title="MXNet.jl" class="md-header-nav__button md-logo">
<i class="md-icon"></i>
</a>
</div>
<div class="md-flex__cell md-flex__cell--shrink">
<label class="md-icon md-icon--menu md-header-nav__button" for="__drawer"></label>
</div>
<div class="md-flex__cell md-flex__cell--stretch">
<div class="md-flex__ellipsis md-header-nav__title" data-md-component="title">
<span class="md-header-nav__topic">
MXNet.jl
</span>
<span class="md-header-nav__topic">
Key-Value Store
</span>
</div>
</div>
<div class="md-flex__cell md-flex__cell--shrink">
<label class="md-icon md-icon--search md-header-nav__button" for="__search"></label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="query" data-md-state="active">
<label class="md-icon md-search__icon" for="__search"></label>
<button type="reset" class="md-icon md-search__icon" data-md-component="reset" tabindex="-1">
&#xE5CD;
</button>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" data-md-scrollfix>
<div class="md-search-result" data-md-component="result">
<div class="md-search-result__meta">
Type to start searching
</div>
<ol class="md-search-result__list"></ol>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="md-flex__cell md-flex__cell--shrink">
<div class="md-header-nav__source">
<a href="https://github.com/apache/mxnet/tree/master/julia#mxnet/" title="Go to repository" class="md-source" data-md-source="github">
<div class="md-source__icon">
<svg viewBox="0 0 24 24" width="24" height="24">
<use xlink:href="#__github" width="24" height="24"></use>
</svg>
</div>
<div class="md-source__repository">
GitHub
</div>
</a>
</div>
</div>
</div>
</nav>
</header>
<div class="md-container">
<main class="md-main" role="main">
<div class="md-main__inner md-grid" data-md-component="container">
<div class="md-sidebar md-sidebar--primary" data-md-component="navigation">
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary" data-md-level="0">
<label class="md-nav__title md-nav__title--site" for="__drawer">
<a href="../.." title="MXNet.jl" class="md-nav__button md-logo">
<i class="md-icon"></i>
</a>
MXNet.jl
</label>
<div class="md-nav__source">
<a href="https://github.com/apache/mxnet/tree/master/julia#mxnet/" title="Go to repository" class="md-source" data-md-source="github">
<div class="md-source__icon">
<svg viewBox="0 0 24 24" width="24" height="24">
<use xlink:href="#__github" width="24" height="24"></use>
</svg>
</div>
<div class="md-source__repository">
GitHub
</div>
</a>
</div>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../.." title="Home" class="md-nav__link">
Home
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-2" type="checkbox" id="nav-2">
<label class="md-nav__link" for="nav-2">
Tutorial
</label>
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
<label class="md-nav__title" for="nav-2">
Tutorial
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../tutorial/mnist/" title="Digit Recognition on MNIST" class="md-nav__link">
Digit Recognition on MNIST
</a>
</li>
<li class="md-nav__item">
<a href="../../tutorial/char-lstm/" title="Generating Random Sentence with LSTM RNN" class="md-nav__link">
Generating Random Sentence with LSTM RNN
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-3" type="checkbox" id="nav-3">
<label class="md-nav__link" for="nav-3">
User Guide
</label>
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
<label class="md-nav__title" for="nav-3">
User Guide
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../user-guide/install/" title="Installation Guide" class="md-nav__link">
Installation Guide
</a>
</li>
<li class="md-nav__item">
<a href="../../user-guide/overview/" title="Overview" class="md-nav__link">
Overview
</a>
</li>
<li class="md-nav__item">
<a href="../../user-guide/faq/" title="FAQ" class="md-nav__link">
FAQ
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--nested">
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-4" type="checkbox" id="nav-4" checked>
<label class="md-nav__link" for="nav-4">
API Documentation
</label>
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
<label class="md-nav__title" for="nav-4">
API Documentation
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../context/" title="Context" class="md-nav__link">
Context
</a>
</li>
<li class="md-nav__item">
<a href="../model/" title="Models" class="md-nav__link">
Models
</a>
</li>
<li class="md-nav__item">
<a href="../initializer/" title="Initializers" class="md-nav__link">
Initializers
</a>
</li>
<li class="md-nav__item">
<a href="../optimizer/" title="Optimizers" class="md-nav__link">
Optimizers
</a>
</li>
<li class="md-nav__item">
<a href="../callback/" title="Callbacks in training" class="md-nav__link">
Callbacks in training
</a>
</li>
<li class="md-nav__item">
<a href="../metric/" title="Evaluation Metrics" class="md-nav__link">
Evaluation Metrics
</a>
</li>
<li class="md-nav__item">
<a href="../io/" title="Data Providers" class="md-nav__link">
Data Providers
</a>
</li>
<li class="md-nav__item">
<a href="../ndarray/" title="NDArray API" class="md-nav__link">
NDArray API
</a>
</li>
<li class="md-nav__item">
<a href="../symbolic-node/" title="Symbolic API" class="md-nav__link">
Symbolic API
</a>
</li>
<li class="md-nav__item">
<a href="../nn-factory/" title="Neural Networks Factory" class="md-nav__link">
Neural Networks Factory
</a>
</li>
<li class="md-nav__item">
<a href="../executor/" title="Executor" class="md-nav__link">
Executor
</a>
</li>
<li class="md-nav__item md-nav__item--active">
<input class="md-toggle md-nav__toggle" data-md-toggle="toc" type="checkbox" id="__toc">
<a href="./" title="Key-Value Store" class="md-nav__link md-nav__link--active">
Key-Value Store
</a>
</li>
<li class="md-nav__item">
<a href="../visualize/" title="Network Visualization" class="md-nav__link">
Network Visualization
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="toc">
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary">
</nav>
</div>
</div>
</div>
<div class="md-content">
<article class="md-content__inner md-typeset">
<a href="https://github.com/apache/mxnet/tree/master/edit/master/docs/api/kvstore.md" title="Edit this page" class="md-icon md-content__icon">&#xE3C9;</a>
<!–- Licensed to the Apache Software Foundation (ASF) under one –> <!–- or more contributor license agreements. See the NOTICE file –> <!–- distributed with this work for additional information –> <!–- regarding copyright ownership. The ASF licenses this file –> <!–- to you under the Apache License, Version 2.0 (the –> <!–- "License"); you may not use this file except in compliance –> <!–- with the License. You may obtain a copy of the License at –>
<!–- http://www.apache.org/licenses/LICENSE-2.0 –>
<!–- Unless required by applicable law or agreed to in writing, –> <!–- software distributed under the License is distributed on an –> <!–- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY –> <!–- KIND, either express or implied. See the License for the –> <!–- specific language governing permissions and limitations –> <!–- under the License. –>
<p><a id='Key-Value-Store-1'></a></p>
<h1 id="key-value-store">Key-Value Store</h1>
<p><a id='MXNet.mx.KVStore' href='#MXNet.mx.KVStore'>#</a>
<strong><code>MXNet.mx.KVStore</code></strong> &mdash; <em>Type</em>.</p>
<pre><code class="julia">KVStore(kv_type = :local)
</code></pre>
<p>For single machine training, there are two commonly used types:</p>
<ul>
<li><code>local</code>: Copies all gradients to CPU memory and updates weights there.</li>
<li><code>device</code>: Aggregates gradients and updates weights on GPU(s). With this setting, the <code>KVStore</code> also attempts to use GPU peer-to-peer communication, potentially accelerating the communication.</li>
</ul>
<p>For distributed training, <code>KVStore</code> also supports a number of types:</p>
<ul>
<li><code>dist_sync</code>: Behaves similarly to <code>local</code> but with one major difference. With <code>dist_sync</code>, batch-size now means the batch size used on each machine. So if there are <code>n</code> machines and we use batch size $b$, then <code>dist_sync</code> behaves like <code>local</code> with batch size <code>n * b</code>.</li>
<li><code>dist_device_sync</code>: Identical to <code>dist_sync</code> with the difference similar to <code>device</code> vs <code>local</code>.</li>
<li><code>dist_async</code>: Performs asynchronous updates. The weights are updated whenever gradients are received from any machine. No two updates happen on the same weight at the same time. However, the order is not guaranteed.</li>
</ul>
<p><a target='_blank' href='https://github.com/apache/mxnet/blob/26a5ad1f39784a60d1564f6f740e5c7bd971cd65/julia/src/kvstore.jl#L20-L45' class='documenter-source'>source</a><br></p>
<p><a id='MXNet.mx.barrier-Tuple{KVStore}' href='#MXNet.mx.barrier-Tuple{KVStore}'>#</a>
<strong><code>MXNet.mx.barrier</code></strong> &mdash; <em>Method</em>.</p>
<pre><code class="julia">barrier(kv::KVStore)
</code></pre>
<p>Invokes global barrier among all worker nodes.</p>
<p>For example, assume there are <code>n</code> machines. We would like machine <code>0</code> to first <code>init</code> the values and then have all the workers <code>pull</code> the initialized value. Before pulling, we can place invoke <code>barrier(kv)</code> to guarantee that the initialization is finished.</p>
<p><a target='_blank' href='https://github.com/apache/mxnet/blob/26a5ad1f39784a60d1564f6f740e5c7bd971cd65/julia/src/kvstore.jl#L237-L246' class='documenter-source'>source</a><br></p>
<p><a id='MXNet.mx.init!-Tuple{KVStore,Int64,NDArray}' href='#MXNet.mx.init!-Tuple{KVStore,Int64,NDArray}'>#</a>
<strong><code>MXNet.mx.init!</code></strong> &mdash; <em>Method</em>.</p>
<pre><code class="julia">init!(kv::KVStore, key::Int, val::NDArray)
init!(kv::KVStore, keys, vals)
</code></pre>
<p>Initializes a single or a sequence of key-value pairs into the store.</p>
<p>For each key, one must <code>init!</code> it before calling <code>push!</code> or <code>pull!</code>. When multiple workers invoke <code>init!</code> for the same key, only the value supplied by worker with rank <code>0</code> is used. This function returns after data has been initialized successfully.</p>
<pre><code class="julia-repl">julia&gt; kv = KVStore(:local)
mx.KVStore @ local
julia&gt; init!(kv, 42, mx.rand(2, 3))
</code></pre>
<p><a target='_blank' href='https://github.com/apache/mxnet/blob/26a5ad1f39784a60d1564f6f740e5c7bd971cd65/julia/src/kvstore.jl#L80-L97' class='documenter-source'>source</a><br></p>
<p><a id='MXNet.mx.pull!-Tuple{KVStore,Int64,NDArray}' href='#MXNet.mx.pull!-Tuple{KVStore,Int64,NDArray}'>#</a>
<strong><code>MXNet.mx.pull!</code></strong> &mdash; <em>Method</em>.</p>
<p>Pulls a single value or a sequence of values from the store.</p>
<p>This function returns immediately after adding an operator to the engine. Subsequent attempts to read from the <code>out</code> variable will be blocked until the pull operation completes.</p>
<p><code>pull</code> is executed asynchronously after all previous <code>pull</code> calls and only the last <code>push</code> call for the same input key(s) are finished.</p>
<p>The returned values are guaranteed to be the latest values in the store.</p>
<p>See <a href="./#MXNet.mx.pull!-Tuple{KVStore,Int64,NDArray}"><code>pull!</code></a> for more examples.</p>
<p><a target='_blank' href='https://github.com/apache/mxnet/blob/26a5ad1f39784a60d1564f6f740e5c7bd971cd65/julia/src/kvstore.jl#L189-L202' class='documenter-source'>source</a><br></p>
<p><a id='MXNet.mx.setoptimizer!-Tuple{KVStore,AbstractOptimizer}' href='#MXNet.mx.setoptimizer!-Tuple{KVStore,AbstractOptimizer}'>#</a>
<strong><code>MXNet.mx.setoptimizer!</code></strong> &mdash; <em>Method</em>.</p>
<pre><code class="julia">setoptimizer!(kv::KVStore, opt)
</code></pre>
<p>Registers an optimizer with the kvstore.</p>
<p>When using a single machine, this function updates the local optimizer. If using multiple machines and this operation is invoked from a worker node, it will serialized the optimizer with pickle and send it to all servers. The function returns after all servers have been updated.</p>
<pre><code class="julia-repl">julia&gt; kv = KVStore()
mx.KVStore @ local
julia&gt; W = mx.zeros(2, 3) # 2×3 weight matrix
2×3 mx.NDArray{Float32,2} @ CPU0:
0.0 0.0 0.0
0.0 0.0 0.0
julia&gt; init!(kv, 42, W)
julia&gt; setoptimizer!(kv, SGD(η = .2)) # SGD with .2 as learning rate
julia&gt; ∇W = mx.ones(2, 3) # assume it's the gradient
2×3 mx.NDArray{Float32,2} @ CPU0:
1.0 1.0 1.0
1.0 1.0 1.0
julia&gt; push!(kv, 42, ∇W)
julia&gt; pull!(kv, 42, W) # fetch weight and write back to `W`
julia&gt; W
2×3 mx.NDArray{Float32,2} @ CPU0:
-0.2 -0.2 -0.2
-0.2 -0.2 -0.2
</code></pre>
<p><a target='_blank' href='https://github.com/apache/mxnet/blob/26a5ad1f39784a60d1564f6f740e5c7bd971cd65/julia/src/kvstore.jl#L300-L337' class='documenter-source'>source</a><br></p>
<p><a id='MXNet.mx.setupdater!-Tuple{KVStore,Any}' href='#MXNet.mx.setupdater!-Tuple{KVStore,Any}'>#</a>
<strong><code>MXNet.mx.setupdater!</code></strong> &mdash; <em>Method</em>.</p>
<pre><code class="julia">setupdater!(kv, updater)
</code></pre>
<p>Sets a <code>push!</code> updater into the store.</p>
<p>This function only changes the local store. When running on multiple machines one must use <code>set_optimizer</code>.</p>
<pre><code class="julia-repl">julia&gt; update(key, val, orig) = mx.@inplace orig += val .* .2
update (generic function with 1 method)
julia&gt; kv = KVStore(:local)
mx.KVStore @ local
julia&gt; mx.setupdater!(kv, update)
julia&gt; init!(kv, 42, mx.ones(2, 3))
julia&gt; push!(kv, 42, mx.ones(2, 3))
julia&gt; x = NDArray(undef, 2, 3);
julia&gt; pull!(kv, 42, x)
julia&gt; x
2×3 mx.NDArray{Float32,2} @ CPU0:
1.2 1.2 1.2
1.2 1.2 1.2
</code></pre>
<p><a target='_blank' href='https://github.com/apache/mxnet/blob/26a5ad1f39784a60d1564f6f740e5c7bd971cd65/julia/src/kvstore.jl#L261-L291' class='documenter-source'>source</a><br></p>
<p><a id='Base.push!-Tuple{KVStore,Int64,NDArray}' href='#Base.push!-Tuple{KVStore,Int64,NDArray}'>#</a>
<strong><code>Base.push!</code></strong> &mdash; <em>Method</em>.</p>
<pre><code class="julia">push!(kv::KVStore, key, val; priority = 0)
push!(kv::KVStore, key, vals; priority = 0)
push!(kv::KVStore, keys, vals; priority = 0)
</code></pre>
<p>Pushes a single or a sequence of key-value pairs into the store.</p>
<p>This function returns immediately after adding an operator to the engine. The actual operation is executed asynchronously. If there are consecutive pushes to the same key, there is no guarantee on the serialization of pushes. The execution of a push does not guarantee that all previous pushes are finished. There is no synchronization between workers by default. One can use $barrier()$ to sync all workers.</p>
<p><code>push!</code> and <code>pull!</code> single <code>NDArray</code>:</p>
<pre><code class="julia-repl">julia&gt; kv = KVStore(:local)
mx.KVStore @ local
julia&gt; x = NDArray(undef, 2, 3);
julia&gt; init!(kv, 3, x)
julia&gt; push!(kv, 3, mx.ones(2, 3) * 8)
julia&gt; pull!(kv, 3, x)
julia&gt; x
2×3 mx.NDArray{Float32,2} @ CPU0:
8.0 8.0 8.0
8.0 8.0 8.0
</code></pre>
<p>Aggregate values and <code>push!</code>:</p>
<pre><code class="julia-repl">julia&gt; vals = [mx.ones((2, 3), gpu(0)) * 3, mx.ones((2, 3), gpu(1)) * 4];
julia&gt; push!(kv, 3, vals)
julia&gt; pull!(kv, 3, x)
julia&gt; x
2×3 mx.NDArray{Float32,2} @ CPU0:
7.0 7.0 7.0
7.0 7.0 7.0
</code></pre>
<p><code>push!</code> a list of key to single device:</p>
<pre><code class="julia-repl">julia&gt; keys = [4, 5];
julia&gt; init!(kv, keys, [NDArray(undef, 2, 3), NDArray(undef, 2, 3)])
julia&gt; push!(kv, keys, [x, x])
julia&gt; y, z = NDArray(undef, 2, 3), NDArray(undef, 2, 3);
julia&gt; pull!(kv, keys, [y, z])
</code></pre>
<p><a target='_blank' href='https://github.com/apache/mxnet/blob/26a5ad1f39784a60d1564f6f740e5c7bd971cd65/julia/src/kvstore.jl#L112-L172' class='documenter-source'>source</a><br></p>
</article>
</div>
</div>
</main>
<footer class="md-footer">
<div class="md-footer-nav">
<nav class="md-footer-nav__inner md-grid">
<a href="../executor/" title="Executor" class="md-flex md-footer-nav__link md-footer-nav__link--prev" rel="prev">
<div class="md-flex__cell md-flex__cell--shrink">
<i class="md-icon md-icon--arrow-back md-footer-nav__button"></i>
</div>
<div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title">
<span class="md-flex__ellipsis">
<span class="md-footer-nav__direction">
Previous
</span>
Executor
</span>
</div>
</a>
<a href="../visualize/" title="Network Visualization" class="md-flex md-footer-nav__link md-footer-nav__link--next" rel="next">
<div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title">
<span class="md-flex__ellipsis">
<span class="md-footer-nav__direction">
Next
</span>
Network Visualization
</span>
</div>
<div class="md-flex__cell md-flex__cell--shrink">
<i class="md-icon md-icon--arrow-forward md-footer-nav__button"></i>
</div>
</a>
</nav>
</div>
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-footer-copyright">
powered by
<a href="https://www.mkdocs.org">MkDocs</a>
and
<a href="https://squidfunk.github.io/mkdocs-material/">
Material for MkDocs</a>
</div>
</div>
</div>
</footer>
</div>
<script src="../../assets/javascripts/application.808e90bb.js"></script>
<script>app.initialize({version:"1.0.4",url:{base:"../.."}})</script>
<script src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML"></script>
<script src="../../assets/mathjaxhelper.js"></script>
</body>
</html>