Skip to content

Commit

Permalink
[demo] Add an option for word hashing vocab and optimize Sparse Neare…
Browse files Browse the repository at this point in the history
…st Centroid Classifier (#91)

* demo: Clarify some pages.
* demo: Support MurmurHash3 for word encoding. Resolves #88
* demo/perceptron: Support adding sparse features.
* demo/ncc: Start to support sparse features.
  • Loading branch information
juharris committed Jun 17, 2020
1 parent e0bbded commit 3d74de8
Show file tree
Hide file tree
Showing 19 changed files with 522 additions and 209 deletions.
2 changes: 2 additions & 0 deletions demo/.soliumignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# This file is in the root folder for VS Code.

# Abstract contracts do not work yet: https://github.com/duaraghav8/Ethlint/issues/281
# grep -R --files-with-matches '^abstract contract' client/src/contracts/
client/src/contracts/classification/Classifier.sol
Expand Down
2 changes: 2 additions & 0 deletions demo/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"@tensorflow-models/universal-sentence-encoder": "^1.2.0",
"@tensorflow/tfjs": "^1.3.2",
"@types/jest": "^24.0.23",
"@types/murmurhash-js": "^1.0.3",
"@types/node": "^12.12.14",
"@types/react": "^16.9.14",
"@types/react-dom": "^16.9.4",
Expand All @@ -21,6 +22,7 @@
"dotenv": "^8.2.0",
"immutability-helper": "^3.0.1",
"moment": "^2.24.0",
"murmurhash-js": "^1.0.0",
"notistack": "^0.9.7",
"react": "^16.12.0",
"react-addons-update": "^15.6.2",
Expand Down
68 changes: 37 additions & 31 deletions demo/client/src/components/addModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,42 @@ class AddModel extends React.Component {
onChange={this.handleInputChange}
/>

{/* Encoder */}
<Typography variant="h6" component="h6">
Encoder
</Typography>
<Typography component="p">
An encoder is the method that is used to convert the input (text, image, etc.) into a machine readable format.
</Typography>
<Select className={this.classes.selector}
onChange={this.handleInputChange}
value={this.state.encoder}
inputProps={{
name: 'encoder',
}}
>
<Tooltip value="none" placement="top-start"
title="No transformation will be applied">
<MenuItem>None</MenuItem>
</Tooltip>
<Tooltip value="MurmurHash3" placement="top-start"
title="Convert each word to a 32-bit number using MurmurHash3">
<MenuItem>MurmurHash3</MenuItem>
</Tooltip>
<Tooltip value="IMDB vocab" placement="top-start"
title="Convert each word in English text to a number using the 1000 most frequent words in the IMDB review dataset">
<MenuItem>IMDB vocab</MenuItem>
</Tooltip>
<Tooltip value="universal sentence encoder" placement="top-start"
title="Use Universal Sentence Encoder to convert English text to a vector of numbers">
<MenuItem>Universal Sentence Encoder (for English text)</MenuItem>
</Tooltip>
<Tooltip value="MobileNetv2" placement="top-start"
title="Use MobileNetv2 to convert images to a vector of numbers">
<MenuItem>MobileNetv2 (for images)</MenuItem>
</Tooltip>
</Select>

{/* Model */}
{/* Don't need to ask for the model type since there is only one option and in the future, it should be inferred from the provided file.
<InputLabel className={this.classes.selectorLabel} htmlFor="model-type">Model type</InputLabel>
Expand Down Expand Up @@ -307,36 +343,6 @@ class AddModel extends React.Component {
)}
</Dropzone>

{/* Encoder */}
<Tooltip placement="top-start"
title="The method that will be used to convert the input (text, image, etc.) into a machine readable format">
<InputLabel className={this.classes.selectorLabel} htmlFor="encoder">Encoder</InputLabel>
</Tooltip>
<Select className={this.classes.selector}
onChange={this.handleInputChange}
value={this.state.encoder}
inputProps={{
name: 'encoder',
}}
>
<Tooltip value="none" placement="top-start"
title="No transformation will be applied">
<MenuItem>None</MenuItem>
</Tooltip>
<Tooltip value="IMDB vocab" placement="top-start"
title="Convert each word in English text to a number using the 1000 most frequent words in the IMDB review dataset">
<MenuItem>IMDB vocab</MenuItem>
</Tooltip>
<Tooltip value="universal sentence encoder" placement="top-start"
title="Use Universal Sentence Encoder to convert English text to a vector of numbers">
<MenuItem>Universal Sentence Encoder (for English text)</MenuItem>
</Tooltip>
<Tooltip value="MobileNetv2" placement="top-start"
title="Use MobileNetv2 to convert images to a vector of numbers">
<MenuItem>MobileNetv2 (for images)</MenuItem>
</Tooltip>
</Select>

{/* Incentive Mechanism */}
<Tooltip placement="top-start"
title={"The system that will be used to determine rewards for data that is determined to be \"good\"."}>
Expand Down Expand Up @@ -387,7 +393,7 @@ class AddModel extends React.Component {
</Paper>
<Paper className={this.classes.root} elevation={1}>
<Typography component="h3">
Deployment Status
Deployment Status
</Typography>
<Table className={this.classes.table} aria-label="Deployment Information Table">
<TableHead>
Expand Down
20 changes: 16 additions & 4 deletions demo/client/src/components/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import * as tf from '@tensorflow/tfjs';
import loadImage from 'blueimp-load-image';
import update from 'immutability-helper';
import moment from 'moment';
import { murmur3 } from 'murmurhash-js';
import { withSnackbar } from 'notistack';
import PropTypes from 'prop-types';
import React from 'react';
Expand Down Expand Up @@ -316,7 +317,11 @@ class Model extends React.Component {
}

async setTransformInputMethod() {
if (this.state.contractInfo.encoder === 'universal sentence encoder') {
let { encoder } = this.state.contractInfo
if (encoder) {
encoder = encoder.toLocaleLowerCase('en')
}
if (encoder === 'universal sentence encoder') {
this.setState({ inputType: INPUT_TYPE_TEXT });
UniversalSentenceEncoder.load().then(use => {
this.transformInput = async (query) => {
Expand All @@ -335,7 +340,7 @@ class Model extends React.Component {
};
this.transformInput = this.transformInput.bind(this);
});
} else if (this.state.contractInfo.encoder === 'MobileNetv2') {
} else if (encoder === 'MobileNetv2'.toLocaleLowerCase('en')) {
this.setState({ inputType: INPUT_TYPE_IMAGE });
// https://github.com/tensorflow/tfjs-models/tree/master/mobilenet
mobilenet.load({
Expand All @@ -362,14 +367,14 @@ class Model extends React.Component {
}
this.transformInput = this.transformInput.bind(this);
});
} else if (this.state.contractInfo.encoder === 'IMDB vocab') {
} else if (encoder === 'IMDB vocab'.toLocaleLowerCase('en')) {
this.setState({ inputType: INPUT_TYPE_TEXT });
this.vocab = [];
Object.entries(ImdbVocab).forEach(([key, value]) => {
this.vocab[value] = key;
});
this.transformInput = async (query) => {
const tokens = query.toLowerCase().split(" ");
const tokens = query.toLocaleLowerCase('en').split(/\s+/)
return tokens.map(t => {
let idx = ImdbVocab[t];
if (idx === undefined) {
Expand All @@ -380,6 +385,13 @@ class Model extends React.Component {
}).map(v => this.web3.utils.toHex(v));
};
this.transformInput = this.transformInput.bind(this);
} else if (encoder === 'MurmurHash3'.toLocaleLowerCase('en')) {
this.setState({ inputType: INPUT_TYPE_TEXT })
this.transformInput = async (query) => {
const tokens = query.toLocaleLowerCase('en').split(/\s+/)
return tokens.map(murmur3).map(v => this.web3.utils.toHex(v));
};
this.transformInput = this.transformInput.bind(this);
} else {
throw new Error(`Couldn't find encoder for ${this.state.contractInfo.encoder}`);
}
Expand Down
16 changes: 6 additions & 10 deletions demo/client/src/containers/modelList.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Button from '@material-ui/core/Button';
import CircularProgress from '@material-ui/core/CircularProgress';
import Container from '@material-ui/core/Container';
import IconButton from '@material-ui/core/IconButton';
import Link from '@material-ui/core/Link';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
Expand All @@ -10,7 +11,6 @@ import Modal from '@material-ui/core/Modal';
import Paper from '@material-ui/core/Paper';
import { withStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import AddIcon from '@material-ui/icons/Add';
import ClearIcon from '@material-ui/icons/Clear';
import DeleteIcon from '@material-ui/icons/Delete';
import { withSnackbar } from 'notistack';
Expand Down Expand Up @@ -233,16 +233,12 @@ class ModelList extends React.Component {
</Typography>
<Typography component="p">
Here you will find models stored on a blockchain that you can interact with.
Models are added to this list if you have recorded them locally in this browser
Models are added to this list if you have recorded them on your device in this browser
{serviceStorageEnabled ? " or if they are listed on a centralized database" : ""}.
</Typography>
<div>
<Button className={this.props.classes.button} variant="outlined" color="primary"
href='/addDeployedModel'
>
<AddIcon />&nbsp;Use a deployed model not listed
</Button>
</div>
<Typography component="p">
You can deploy your own model <Link href='/addModel'>here</Link> or use an already deployed model by filling in the information <Link href='/addDeployedModel'>here</Link>.
</Typography>
</div>
{this.state.loadingModels ?
<div className={this.props.classes.spinnerDiv}>
Expand Down Expand Up @@ -277,7 +273,7 @@ class ModelList extends React.Component {
:
<div className={this.props.classes.descriptionDiv}>
<Typography component="p">
No models found.
You do not have any models listed.
</Typography>
</div>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ contract NaiveBayesClassifier is Classifier64 {
* @param _classifications The classifications supported by the model.
* @param _classCounts The number of occurrences of each class in the training data.
* @param _featureCounts For each class, the number of times each feature occurs within that class.
* Each innermost list is a tuple of the feature index and the number of times that feature occurs within the class.
* Each innermost array is a tuple of the feature index and the number of times that feature occurs within the class.
* @param _totalNumFeatures The total number of features throughout all classes.
* @param _smoothingFactor The smoothing factor (sometimes called alpha). Use toFloat (1 mapped) for Laplace smoothing.
*/
Expand Down Expand Up @@ -97,7 +97,8 @@ contract NaiveBayesClassifier is Classifier64 {
* Set feature counts for an existing classification.
* For efficiency, features are overriden them if they have already been set.
* Made to be called just after the contract is created and never again.
* @param _featureCounts The index to start placing `_weights` into the model's weights.
* @param _featureCounts The number of times each feature occurs.
* Each innermost array is a tuple of the feature index and the number of times that feature occurs within the class.
* @param classification The class to add counts to.
*/
function initializeCounts(uint32[][] memory _featureCounts, uint64 classification) public onlyOwner {
Expand Down
Loading

0 comments on commit 3d74de8

Please sign in to comment.