Related
I am interested in developing a logit-based choice model using Tensorflow.
I am fairly new to this tool, so I was wondering if there is a way to get the statistics (i.e., the p-value) of the weights obtained from Tensorflow , just like someone would get from Stata or SPSS.
The code does run, but cannot be sure if the model is valid unless I can compare the p-values of the variables from the estimation result from STATA.
The data structure is simple; it's a form of a survey, where a respondent chooses an alternative out of 4 options, each with different feature levels (a.k.a. a conjoint analysis).
(I am trying something new; that's why I am not using pylogit of xlogit packages.)
Below is the code I wrote:
mport numpy as np
import tensorflow as tf
import pandas as pd
from sklearn.model_selection import train_test_split
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
np.random.seed(0)
tf.random.set_seed(0)
variables = pd.read_excel('file.xls')
target_vars = ['A','B','C','D','E']
X = pd.DataFrame()
for i in target_vars:
X[i]=variables[i]
y = variables['choice']
X_tn, X_te, y_tn, y_te = train_test_split(X, y, random_state=0)
n_feat = X_tn.shape[1]
epo = 100
model = Sequential()
model.add(Dense(1, input_dim=n_feat, activation='sigmoid'))
model.add(Dense(1))
model.compile(loss = 'mean_squared_error',
optimizer = 'adam',
metrics = ['mean_squared_error'])
hist = model.fit(X_tn, y_tn, epochs=epo, batch_size=4)
model.summary()
model.get_weights()
some other optional questions only if you are familiar with discrete choice models...
i) the original dataset is a conjoint survey with 4 alternatives at each choice situation - that's why I put batch_size=4. Am I doing it right?
ii) have I set the epoch too large?
First of all your question is about p-value significant where they are significant againts all input data in scopes !
The idea is you may applied many of the functions or custom functions but avtivation layer is asynchornize or fairly chances based on your target.
( 1 ) You can have model with 2-classes, 4-classes or 10 classes output to perform simiarlities significant or maximum, minumum, average maximum or last changes based on your selected function.
( 2 ) Prediction is a result from your input and none sigficant, significant relationship learning develop.
( 3 ) Compares of them possibile by make it into same ranges of expectation otherwises it is value for it subset.
sample output:
F:\temp\Python>python test_read_excel.py
0 1 2 3 4 5
0 1 0 0 0 0 0
1 0 1 0 0 0 0
2 0 0 1 0 0 0
3 0 0 0 1 0 0
4 0 0 0 0 1 0
5 0 0 0 0 0 1
(6, 6)
none significant:
[array([[-0.6489598]], dtype=float32), array([-0.0998228], dtype=float32), array([[1.7546983e-05]], dtype=float32), array([-3.6847262e-06], dtype=float32)]
** sample code **
variables = pd.read_excel('F:\\temp\\20220305\\Book 2.xlsx', index_col=None, header=None)
list_of_X = [ ]
list_of_Y = [ ]
for i in range(np.asarray(variables).shape[0]):
for j in range(np.asarray(variables).shape[1]):
if variables[j][i] == "X" :
print('found: ' + str(i) + ":" + str(j))
list_of_X.append(i)
list_of_Y.append(1)
else :
list_of_X.append(i)
list_of_Y.append(0)
list_of_X = np.reshape(list_of_X, (1, 36, 1))
list_of_Y = np.reshape(list_of_Y, (1, 36, 1))
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""
: Model Initialize
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""
model = tf.keras.models.Sequential([
tf.keras.layers.InputLayer(input_shape=(36, 1)),
tf.keras.layers.Dense(1 , activation='sigmoid' ),
])
model.add(tf.keras.layers.Dense(1))
model.summary()
model.compile(loss = 'mean_squared_error',
optimizer = 'adam',
metrics = ['mean_squared_error'])
history = model.fit(list_of_X, list_of_Y, epochs=1000, batch_size=4)
What is the tf.keras equivalent of encoder.get_feature_names found in sklearn? As shown shown in this SO question
Need this to get all the one-hot encoded feature names created "from tensorflow.keras.layers.experimental import preprocessing". I will appreciate any post with example.
Here is a way to do this. A few relevant notes:
I'm using the California housing dataset from scikit-learn, I haven't included that portion of the code, but train_ds is already split into X, y and the dataset is already set up.
I setup up this particular feature_layer using feature_columns, which I know is deprecated in favor of preprocessing layers in keras. It was instantiated using:
feature_layer = tf.keras.layers.DenseFeatures(
dictionary_of_all_feature_columns.values())
I don't believe this should affect the use of the function below in other contexts, but I'll test it as I get to preprocessing layers in Keras. It also only handles numeric, one-hot encoded, and bucketized feature columns. I'm at the beginning of my study of feature engineering, so have not experienced too much diversity on variable types. Will update here as I make progress over there.
import pandas as pd
# Get one batch of what we're going to process
[(X, y)] = train_ds.take(1)
def get_feature_names(feature_layer):
"""
Takes as input a preprocessing layer of type
keras.feature_column.dense_features_v2.DenseFeatures
and returns as output a list containing variable names in the order that
they are processed by the layer. Only deals with numeric and categorical
variables. The one-hot encoded variable names match how scikit-learn
names them, namely variable_name + _ + category_in_vocabulary_list and similarly with bucket ranges.
"""
feature_list = []
feature_layer_dictionary = feature_layer.get_config()
list_of_encoded_features = feature_layer_dictionary["feature_columns"]
for encoded_feature in list_of_encoded_features:
class_name = encoded_feature["class_name"]
if class_name == 'NumericColumn':
feature_list.append(encoded_feature['config']['key'])
elif class_name == 'IndicatorColumn':
variable_name = encoded_feature['config'][
'categorical_column']['config']['key']
category_list = list(
encoded_feature['config']
['categorical_column']['config']
['vocabulary_list'])
for category in category_list:
feature_list.append(variable_name + "_" + category)
elif class_name == 'BucketizedColumn':
variable_name = encoded_feature['config'][
'source_column']['config']['key']
boundary_list = list(encoded_feature['config']['boundaries'])
boundary_list.insert(0, -np.inf)
boundary_list.append(np.inf)
for i in range(len(boundary_list) - 1):
begin_bucket = str(boundary_list[i])
end_bucket = str(boundary_list[i+1])
both_sides_bucket = begin_bucket + "_" + end_bucket
encoded_variable_name = variable_name + "_" + both_sides_bucket
feature_list.append(encoded_variable_name)
else:
pass
return feature_list
preprocessed_df = pd.DataFrame(X, columns=list(X.keys()))
postprocessed_df = pd.DataFrame(
feature_layer(X),
columns=get_feature_names(feature_layer))
[ins] In [344]: preprocessed_df.iloc[0]
Out[344]:
longitude -119.3
latitude 36.34
housing_median_age 45.0
total_rooms 3723.0
total_bedrooms 831.0
population 2256.0
households 770.0
median_income 1.8299
ocean_proximity b'INLAND'
Name: 0, dtype: object
[ins] In [345]: postprocessed_df.iloc[0]
Out[345]:
households 0.707083
housing_median_age_-inf_2 0.000000
housing_median_age_2_5 0.000000
housing_median_age_5_10 0.000000
housing_median_age_10_15 0.000000
housing_median_age_15_20 0.000000
housing_median_age_20_30 0.000000
housing_median_age_30_40 0.000000
housing_median_age_40_inf 1.000000
latitude 0.334551
longitude 0.132462
median_income -1.066948
ocean_proximity_<1H OCEAN 0.000000
ocean_proximity_INLAND 1.000000
ocean_proximity_NEAR OCEAN 0.000000
ocean_proximity_NEAR BAY 0.000000
ocean_proximity_ISLAND 0.000000
population 0.724704
total_bedrooms 0.697491
total_rooms 0.501156
Name: 0, dtype: float32
Can someone please direct me to a tutorial provide a starting idea for the problem given below.
I have a mapping of Authors to co authors given as follows:
mapping
>>
{0: [2860, 3117],
1: [318, 1610, 1776, 1865, 2283, 2507, 3076, 3108, 3182, 3357, 3675, 4040],
2: [164, 413, 1448, 1650, 3119, 3238],
} # this is just sample
link_attributes.iloc[:5,:7]
>>
first id keyword_0 keyword_10 keyword_13 keyword_15 keyword_2
0 4 0 1.0 1.0 1.0 1.0
1 9 1 1.0 1.0 1.0 1.0
2 7 2 1.0 NaN 1.0 1.0
3 6 3 1.0 1.0 NaN 1.0
4 9 4 1.0 1.0 1.0 1.0
I have to predict the probability of having a link between a Source and Sink
For example if I am given a Source=13 and Sink=31 then I have to find the probability of having a link between 13 and 31. All the links are un-directed.
import json
import numpy
from keras import Sequential
from keras.layers import Dense
def get_keys(data, keys): # get all keys from json file
if isinstance(data, list):
for item in data:
get_keys(item, keys)
if isinstance(data, dict):
sub_keys = data.keys()
for sub_key in sub_keys:
keys.append(sub_key)
# get all keys, each key is a feature of instances
json_data = open("nodes.json") # read 4016 instances
jdata = json.load(json_data)
keys = []
get_keys(jdata, keys)
keys = set(keys)
print(set(keys))
def build_instance(json_object): # use to build instance from json object, ex: instance = [f0,f1,f2,f3,....f404]
features = []
features.append(json_object.get('id'))
for key in keys:
value = json_object.get(key)
if value is None:
value = 0
elif key == 'id':
continue
features.append(value)
return features
# read all instances and format them, each instance will be [f0,f1, f2,...], as i read from json file, each instance will have 405 features
instances = []
num_of_instances = 0
for item in jdata:
features = build_instance(item)
instances.append(features)
num_of_instances = num_of_instances + 1
print(num_of_instances)
# read "author_id - co author ids" file
traintxt = open('train.txt', 'r')
lines = traintxt.readlines()
au_vs_co_auth_list = []
for line in lines:
line = line.split('\t', 200)
print(line)
# convert value from string to int
string = line[0] # example line[0] = '14 445'
id_vs_coauthor = string.split(" ", 200)
id = id_vs_coauthor[0]
co_author = id_vs_coauthor[1]
line[0:1] = [int(id), int(co_author)]
for i in range(2, len(line)):
line[i] = int(line[i])
au_vs_co_auth_list.append(line)
print(len(au_vs_co_auth_list)) # we have 4016 authors
X_train = []
Y_train = []
generated_train_pairs = []
train_num = 30000 # choose 30000 random training instances
for i in range(train_num):
print(i)
index1 = numpy.random.randint(0, len(au_vs_co_auth_list), 1)[0]
co_authors_of_index1 = au_vs_co_auth_list[index1]
author_id_of_index_1 = au_vs_co_auth_list[index1][0]
if index1 % 2 == 0: # try to create a sample that two author is not related
index2 = numpy.random.randint(0, len(au_vs_co_auth_list), 1)[0]
author_id_of_index_2 = au_vs_co_auth_list[index2][0]
# make sure id1 != id2 and auth 1 and auth2 are not related
while (index1 == index2) or (author_id_of_index_2 in co_authors_of_index1):
index2 = numpy.random.randint(0, len(au_vs_co_auth_list), 1)[0]
author_id_of_index_2 = au_vs_co_auth_list[index2][0]
y = [0, 1] # [relative=FALSE,non-related = TRUE]
else: # try to create a sample that two author is related
author_id_of_index_2 = numpy.random.randint(1, len(co_authors_of_index1),size=1)[0]
y = [1, 0] # [relative=TRUE,non-related = FALSE]
x = instances[author_id_of_index_1][1:] + instances[author_id_of_index_2][
1:] # x = [feature1, feature2,...feature404',feature1', feature2',...feature404']
X_train.append(x)
Y_train.append(y)
X_train = numpy.asarray(X_train)
Y_train = numpy.asarray(Y_train)
print(X_train.shape)
print(Y_train.shape)
# now we have x_train, y_train, build model right now
model = Sequential()
model.add(Dense(512, input_shape=X_train[0].shape, activation='relu'))
model.add(Dense(128, activation='relu'))
model.add(Dense(32, activation='relu'))
model.add(Dense(8, activation='relu'))
model.add(Dense(2, activation='sigmoid'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
model.fit(X_train, Y_train, batch_size=512, epochs=3, verbose=2)
model.save("model.h5")
# now to predict probability of linking between two author ids
id1 = 11 # just random
id2 = 732 # just random
author1 = None
author2 = None
for item in jdata:
if item.get('id') == id1:
author1 = build_instance(item)
if item.get('id') == id2:
author2 = build_instance(item)
if author1 is not None and author2 is not None:
break
x_test = author1[1:] + author2[1:]
x_test = numpy.expand_dims(numpy.asarray(x_test), axis=0)
probability = model.predict(x_test)
print("author id ", id1, " and author id ", id2, end=" ")
if probability[0][1] > probability[0][0]:
print("Not related")
else:
print("Related")
print(probability)
Output:
author id 11 and author id 732 related
Before diving into how to find a solution, I recommend understand your data well, and spend a good part of your time digesting the problem and preparing a dataset.
So from the scenario you described it seems to me your problem is given two Nodes and their attributes predict if there is a link this can interpreted as a binary classification task. I will provide an initial minimalistic simple solution.
what confused me is that you mentioned you have only link_attributes.iloc[:5,:7] link_attributes but not node attributes. In the case you have node attributes it makes more sense because then we just make a combinations of pairs of nodes, and label the pairs wich are not connected as 0 or not_connected and the ones connected as 1 or connected.
So let's make a dataset. As I'm didn't exactly understand what the link attributes mean, let's generate some random data but we can adapt a better example if you edit your question with more details about your data.
About creating a Dataset
For every nodes in the mapping we will create 10 dummy random columns just for the sake of demonstrating.
Then we will create a list of all authors and coauthor called list_of_authors and generate pairs out of this calling it pair_of_authors.
for every pair of authors we will label them as linked or not linked using mapping, for that I created a function called check_if_pair_is_linked.
after this I will show how to create a simple baseline solution for the task. We will use scikit-learn with has a big list of easy to use models for classification.
Code
I folded the code and describe it in 3 major simple steps:
prepare your inputs to create a dataset (using mappings and attributes)
create dataset (for every pair of authors, label then as linked or not and concatenate their attributes)
Use sci-kit learn to fit, predict and evaluate a a model
import itertools
import pandas as pd
import numpy as np
from sklearn.metrics import classification_report
from sklearn import svm
######## first preparing data to create a dataset
+-- 17 lines: ### we already have mappings {---------------------------------------------------------------------------------------------------
This part creates -----> mappings => {
0:[coauthor12,coauthor17231,...],
1:[...],
...,
732: [...]
}
i author_attributes => {
0:[a0_1,attr0_2,...,attr0_10],
1:[a1_1,attr1_2,...,attr1_10],
...,
732: [...]
}
#### Generating our Dataset and preparing dataset to the scikit-learn library(and most other) format
### The idea is generating pairs of authors regardless if they're linked or not and label if a pair is linked
+-- 24 lines: {--------------------------------------------------------------------------------------------------------------------------------
This part creates, a list of pairs of authors containing (attributes_of_both_authors, is_linked_label)
-----> dataset = [
([a0_1,...,a0_10,a1_1,...,a1_10],label_pair0_1)),
([a0_1,...,a0_10,a2_1,...,a2_10],label_pair1_2),
...
([a142_1,...,a142_10,a37_1,...,a37_10],label_pair142_37),
]
#### Training and evaluating a simple machine learning solution
+-- 12 lines: ---------------------------------------------------------------------------------------------------------------------------------This part outputs a report about the model after training the model with a training dataset and evaluate the model in a test dataset (I used the same train data and test data but dont ever do that in a real scenario)
-----> precision recall f1-score support
0 0.93 1.00 0.96 466
1 1.00 0.10 0.18 40
Solution:
import itertools
import pandas as pd
import numpy as np
from sklearn.metrics import classification_report
from sklearn import svm
######## first preparing data to create a dataset
#### we already have mappings
def generate_random_author_attributes(mapping):
author_attributes = {}
for author in mapping.keys():
author_attributes[author] = np.random.random(10).tolist()
for coauthors in mapping.values():
for coauthor in coauthors:
if author_attributes.get(coauthor, False):
pass # check if this coauthor was alredy added
else:
author_attributes[coauthor] = np.random.random(10).tolist()
return author_attributes
mapping = {
0: [2860, 3117],
1: [318, 1610, 1776, 1865, 2283, 2507, 3076, 3108, 3182, 3357, 3675, 4040],
2: [164, 413, 1448, 1650, 3119, 3238],
}
#### hopefully you have attributes like this, for each author you have some attributes, I created 10 random values for each for demonstrating
author_attributes = generate_random_author_attributes(mapping)
#### Generating our Dataset and preparing dataset to the scikit-learn library(and most other) format
### The idea is generating pairs of authors regardless if they're linked or not and label if a pair is linked
def check_if_pair_is_linked(pair_of_authors, mapping):
''' return 1 if linked, 0 if not linked'''
coauthors_of_author0 = mapping.get(pair_of_authors[0],[])
coauthors_of_author1 = mapping.get(pair_of_authors[1],[])
if(pair_of_authors[1] in coauthors_of_author0) or (pair_of_authors[0] in coauthors_of_author1):
return 1
else:
return 0
def create_dataset(author_attributes, mapping):
list_of_all_authors = list(itertools.chain.from_iterable([coauthors for coauthors in mapping.values()]))
list_of_all_authors.extend(mapping.keys())
dataset = []
for pair_of_authors in itertools.permutations(list_of_all_authors, 2):
pair_label = check_if_pair_is_linked(pair_of_authors, mapping)
pair_attributes = author_attributes[pair_of_authors[0]] + author_attributes[pair_of_authors[1]]
dataset.append((pair_attributes,pair_label))
return dataset
dataset=create_dataset(author_attributes, mapping)
X_train = [pair_attributes for pair_attributes,_ in dataset]
y_train = [pair_label for _,pair_label in dataset]
#### Training and evaluating a simple machine learning solution
binary_classifier = svm.SVC()
binary_classifier.fit(X_train, y_train)
#### Checking if the model is good
X_test = X_train # never use you train data as test data
y_test = y_train
true_pairs_label = y_test
predicted_pairs_label = binary_classifier.predict(X_test)
print(classification_report(true_pairs_label, predicted_pairs_label))
OUTPUT
precision recall f1-score support
0 0.93 1.00 0.96 466
1 1.00 0.15 0.26 40
accuracy 0.93 506
macro avg 0.97 0.57 0.61 506
weighted avg 0.94 0.93 0.91 506
After reading the following data:
Head:
Open Close High Low Volume volume_adi volume_obv volume_obvm ... momentum_stoch momentum_stoch_signal momentum_wr momentum_ao others_dr others_dlr others_cr nextClose
0 118.940002 118.950996 119.015999 118.926003 3468.199951 -1468.002197 0.000000 0.000000 ... 27.777779 27.777779 -72.222221 0.000000 14.749734 0.000000 0.000000 118.948997
1 118.954002 118.959000 118.974998 118.892998 3083.300049 1139.846680 3083.300049 -8.533334 ... 53.658535 35.663956 -46.341465 0.000000 0.008407 0.008407 0.006725 118.975998
2 118.966003 118.975998 118.990997 118.922997 2914.600098 3508.808105 2914.600098 722.250000 ... 67.479675 48.897923 -32.520325 0.000000 0.014291 0.014290 0.021017 118.985001
3 118.992996 118.985001 119.000000 118.967003 3088.800049 1909.547119 3088.800049 1195.560059 ... 74.796745 65.311653 -25.203253 0.000000 0.007565 0.007564 0.028583 118.987999
4 118.987999 118.987999 119.001999 118.953003 3175.399902 1641.685669 3175.399902 1525.533325 ... 77.235771 73.170731 -22.764227 -0.001633 0.002521 0.002521 0.031105 118.984001
As this:
column_names = ['Open', 'Close', ... , 'others_cr', 'nextClose']
dataset = pd.read_csv(dataset_path, names=column_names,
na_values = '?', comment='\t', index_col=False,
sep=',', skipinitialspace=True, skiprows=[1], dtype='float32')
print('Head:\n {}'.format(dataset.head()))
I got the following error when trying to split the data and adding a new dimension like:
train_size = int(len(dataset) * 0.67)
train_dataset = dataset[0:train_size,:]
Error:
TypeError: '(slice(0, 201617, 1), slice(None, None, None))' is an invalid key
Any help would be appreciated, thanks in advance.
I was trying to use a numpy splitting technique on a pandas.dataframe
solved it through converting the dataframe into a numpy array using:
dt = dataset.values
dt = dt.astype('float32')
train_size = int(len(dt) * 0.67)
train_dataset = dt[0:train_size,:]
You'd better make sure you split the data in a random manner.
import random
import pandas as pd
# Get the training data size
train_size = int(dataset.shape[0] * 0.67) # dataset.shape[0] is how many rows the dataset have
# randomly choose the training data from dataset
train_loc = random.sample(range(dataset.shape[0]), train_size) # get the the rows' location
train_dataset = dataset.loc[train.loc, :] # get the traininig dataset
test_dataset = dataset.drop(train_size, axis=0) # get the remaining of dataset as the test dataset
You also can use the scikit-learn to split the dataset
sklearn.model_selection.train_test_split
I am relatively new to machine learning as well as tensorflow. I would like to train the data so that predictions with 2 targets and multiple classes could be made. Is this something that can be done? I was able to implement the algorithm for 1 target but don't know how I need to do it for a second target as well.
An example dataset:
DayOfYear Temperature Flow Visibility
316 8 1 4
285 -1 1 4
326 8 2 5
323 -1 0 3
10 7 3 6
62 8 0 3
56 8 1 4
347 7 2 5
363 7 0 3
77 7 3 6
1 7 1 4
308 -1 2 5
364 7 3 6
If I train (DayOfYear Temperature Flow) I can predict the Visibility quite well. But I need to predict Flow as well somehow. I am pretty sure that Flow will influence Visibility so I am not sure how to go with that.
This is the implementation that I have
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import os
import urllib
import numpy as np
import tensorflow as tf
# Data sets
TRAINING = "/ml_baetterich_learn.csv"
TEST = "/ml_baetterich_test.csv"
VALIDATION = "/ml_baetterich_validation.csv"
def main():
# Load datasets.
training_set = tf.contrib.learn.datasets.base.load_csv_without_header(
filename=TRAINING,
target_dtype=np.int,
features_dtype=np.int,
target_column=-1)
test_set = tf.contrib.learn.datasets.base.load_csv_without_header(
filename=TEST,
target_dtype=np.int,
features_dtype=np.int,
target_column=-1)
validation_set = tf.contrib.learn.datasets.base.load_csv_without_header(
filename=VALIDATION,
target_dtype=np.int,
features_dtype=np.int,
target_column=-1)
# Specify that all features have real-value data
feature_columns = [tf.contrib.layers.real_valued_column("", dimension=3)]
# Build 3 layer DNN with 10, 20, 10 units respectively.
classifier = tf.contrib.learn.DNNClassifier(feature_columns=feature_columns,
hidden_units=[10, 20, 10],
n_classes=9,
model_dir="/tmp/iris_model")
# Define the training inputs
def get_train_inputs():
x = tf.constant(training_set.data)
y = tf.constant(training_set.target)
return x, y
# Fit model.
classifier.fit(input_fn=get_train_inputs, steps=4000)
# Define the test inputs
def get_test_inputs():
x = tf.constant(test_set.data)
y = tf.constant(test_set.target)
return x, y
# Define the test inputs
def get_validation_inputs():
x = tf.constant(validation_set.data)
y = tf.constant(validation_set.target)
return x, y
# Evaluate accuracy.
accuracy_test_score = classifier.evaluate(input_fn=get_test_inputs,
steps=1)["accuracy"]
accuracy_validation_score = classifier.evaluate(input_fn=get_validation_inputs,
steps=1)["accuracy"]
print ("\nValidation Accuracy: {0:0.2f}\nTest Accuracy: {1:0.2f}\n".format(accuracy_validation_score,accuracy_test_score))
# Classify two new flower samples.
def new_samples():
return np.array(
[[327,8,3],
[47,8,0]], dtype=np.float32)
predictions = list(classifier.predict_classes(input_fn=new_samples))
print(
"New Samples, Class Predictions: {}\n"
.format(predictions))
if __name__ == "__main__":
main()
Option 1: multi-headed model
You could use a multi-headed DNNEstimator model. This treats Flow and Visibility as two separate softmax classification targets, each with their own set of classes. I had to modify the load_csv_without_header helper function to support multiple targets (which could be cleaner, but is not the point here - feel free to ignore its details).
import numpy as np
import tensorflow as tf
from tensorflow.python.platform import gfile
import csv
import collections
num_flow_classes = 4
num_visib_classes = 7
Dataset = collections.namedtuple('Dataset', ['data', 'target'])
def load_csv_without_header(fn, target_dtype, features_dtype, target_columns):
with gfile.Open(fn) as csv_file:
data_file = csv.reader(csv_file)
data = []
targets = {
target_cols: []
for target_cols in target_columns.keys()
}
for row in data_file:
cols = sorted(target_columns.items(), key=lambda tup: tup[1], reverse=True)
for target_col_name, target_col_i in cols:
targets[target_col_name].append(row.pop(target_col_i))
data.append(np.asarray(row, dtype=features_dtype))
targets = {
target_col_name: np.array(val, dtype=target_dtype)
for target_col_name, val in targets.items()
}
data = np.array(data)
return Dataset(data=data, target=targets)
feature_columns = [
tf.contrib.layers.real_valued_column("", dimension=1),
tf.contrib.layers.real_valued_column("", dimension=2),
]
head = tf.contrib.learn.multi_head([
tf.contrib.learn.multi_class_head(
num_flow_classes, label_name="Flow", head_name="Flow"),
tf.contrib.learn.multi_class_head(
num_visib_classes, label_name="Visibility", head_name="Visibility"),
])
classifier = tf.contrib.learn.DNNEstimator(
feature_columns=feature_columns,
hidden_units=[10, 20, 10],
model_dir="iris_model",
head=head,
)
def get_input_fn(filename):
def input_fn():
dataset = load_csv_without_header(
fn=filename,
target_dtype=np.int,
features_dtype=np.int,
target_columns={"Flow": 2, "Visibility": 3}
)
x = tf.constant(dataset.data)
y = {k: tf.constant(v) for k, v in dataset.target.items()}
return x, y
return input_fn
classifier.fit(input_fn=get_input_fn("tmp_train.csv"), steps=4000)
res = classifier.evaluate(input_fn=get_input_fn("tmp_test.csv"), steps=1)
print("Validation:", res)
Option 2: multi-labeled head
If you keep your CSV data separated by commas, and keep the last column for all the classes a row might have (separated by some token such as space), you can use the following code:
import numpy as np
import tensorflow as tf
all_classes = ["0", "1", "2", "3", "4", "5", "6"]
def k_hot(classes_col, all_classes, delimiter=' '):
table = tf.contrib.lookup.index_table_from_tensor(
mapping=tf.constant(all_classes)
)
classes = tf.string_split(classes_col, delimiter)
ids = table.lookup(classes)
num_items = tf.cast(tf.shape(ids)[0], tf.int64)
num_entries = tf.shape(ids.indices)[0]
y = tf.SparseTensor(
indices=tf.stack([ids.indices[:, 0], ids.values], axis=1),
values=tf.ones(shape=(num_entries,), dtype=tf.int32),
dense_shape=(num_items, len(all_classes)),
)
y = tf.sparse_tensor_to_dense(y, validate_indices=False)
return y
def feature_engineering_fn(features, labels):
labels = k_hot(labels, all_classes)
return features, labels
feature_columns = [
tf.contrib.layers.real_valued_column("", dimension=1), # DayOfYear
tf.contrib.layers.real_valued_column("", dimension=2), # Temperature
]
classifier = tf.contrib.learn.DNNEstimator(
feature_columns=feature_columns,
hidden_units=[10, 20, 10],
model_dir="iris_model",
head=tf.contrib.learn.multi_label_head(n_classes=len(all_classes)),
feature_engineering_fn=feature_engineering_fn,
)
def get_input_fn(filename):
def input_fn():
dataset = tf.contrib.learn.datasets.base.load_csv_without_header(
filename=filename,
target_dtype="S100", # strings of length up to 100 characters
features_dtype=np.int,
target_column=-1
)
x = tf.constant(dataset.data)
y = tf.constant(dataset.target)
return x, y
return input_fn
classifier.fit(input_fn=get_input_fn("tmp_train.csv"), steps=4000)
res = classifier.evaluate(input_fn=get_input_fn("tmp_test.csv"), steps=1)
print("Validation:", res)
We are using DNNEstimator with a multi_label_head, which uses sigmoid crossentropy rather than softmax crossentropy as a loss function. This means that each of the output units/logits are passed through the sigmoid function, which gives the likelihood of the data point belonging to that class, i.e. the classes are computed independently and are not mutually exclusive as they are with softmax crossentropy. This means that you could have between 0 and len(all_classes) classes set for each row in the training set and final predictions.
Also notice that the classes are represented as strings (and k_hot makes the conversion to token indices), so that you could use arbitrary class identifiers such as category UUIDs in e-commerce settings. If the categories in the 3rd and 4th column are different (Flow ID 1 != Visibility ID 1), you could prepend the column name to each class ID, e.g.
316,8,flow1 visibility4
285,-1,flow1 visibility4
326,8,flow2 visibility5
For a description of how k_hot works, see my other SO answer. I decided to use k_hot as a separate function (rather than define it directly in feature_engineering_fn because it's a distinct piece of functionality, and probably TensorFlow will soon have a similar utility function.
Note that if you're now using the first two columns to predict the last two columns, your accuraccy will certainly go down, as the last two columns are highly correlated and using one of them will give you a lot of information about the other. Actually, your code was using only the 3rd column, which was kind of a cheat anyway if the goal is to predict the 3rd and 4th columns.