| @@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TensorFlowNET.Examples", "t | |||
| EndProject | |||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TensorFlowNET.Core", "src\TensorFlowNET.Core\TensorFlowNET.Core.csproj", "{FD682AC0-7B2D-45D3-8B0D-C6D678B04144}" | |||
| EndProject | |||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NumSharp.Core", "..\NumSharp\src\NumSharp.Core\NumSharp.Core.csproj", "{265765E1-C746-4241-AF2B-39B8045292D8}" | |||
| EndProject | |||
| Global | |||
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
| Debug|Any CPU = Debug|Any CPU | |||
| @@ -27,6 +29,10 @@ Global | |||
| {FD682AC0-7B2D-45D3-8B0D-C6D678B04144}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
| {FD682AC0-7B2D-45D3-8B0D-C6D678B04144}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
| {FD682AC0-7B2D-45D3-8B0D-C6D678B04144}.Release|Any CPU.Build.0 = Release|Any CPU | |||
| {265765E1-C746-4241-AF2B-39B8045292D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||
| {265765E1-C746-4241-AF2B-39B8045292D8}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
| {265765E1-C746-4241-AF2B-39B8045292D8}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
| {265765E1-C746-4241-AF2B-39B8045292D8}.Release|Any CPU.Build.0 = Release|Any CPU | |||
| EndGlobalSection | |||
| GlobalSection(SolutionProperties) = preSolution | |||
| HideSolutionNode = FALSE | |||
| @@ -62,4 +62,8 @@ Add Word2Vec example.</PackageReleaseNotes> | |||
| <Folder Include="Keras\Initializers\" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <ProjectReference Include="..\..\..\NumSharp\src\NumSharp.Core\NumSharp.Core.csproj" /> | |||
| </ItemGroup> | |||
| </Project> | |||
| @@ -6,7 +6,7 @@ using System.Linq; | |||
| using System.Text; | |||
| using System.Text.RegularExpressions; | |||
| namespace TensorFlowNET.Examples.CnnTextClassification | |||
| namespace TensorFlowNET.Examples | |||
| { | |||
| public class DataHelpers | |||
| { | |||
| @@ -90,5 +90,65 @@ namespace TensorFlowNET.Examples.CnnTextClassification | |||
| str = Regex.Replace(str, @"\'s", " \'s"); | |||
| return str; | |||
| } | |||
| /// <summary> | |||
| /// Padding | |||
| /// </summary> | |||
| /// <param name="sequences"></param> | |||
| /// <param name="pad_tok">the char to pad with</param> | |||
| /// <returns>a list of list where each sublist has same length</returns> | |||
| public static (int[][], int[]) pad_sequences(int[][] sequences, int pad_tok = 0) | |||
| { | |||
| int max_length = sequences.Select(x => x.Length).Max(); | |||
| return _pad_sequences(sequences, pad_tok, max_length); | |||
| } | |||
| public static (int[][][], int[][]) pad_sequences(int[][][] sequences, int pad_tok = 0) | |||
| { | |||
| int max_length_word = sequences.Select(x => x.Select(w => w.Length).Max()).Max(); | |||
| int[][][] sequence_padded; | |||
| var sequence_length = new int[sequences.Length][]; | |||
| for (int i = 0; i < sequences.Length; i++) | |||
| { | |||
| // all words are same length now | |||
| var (sp, sl) = _pad_sequences(sequences[i], pad_tok, max_length_word); | |||
| sequence_length[i] = sl; | |||
| } | |||
| int max_length_sentence = sequences.Select(x => x.Length).Max(); | |||
| (sequence_padded, _) = _pad_sequences(sequences, np.repeat(pad_tok, max_length_word).Data<int>(), max_length_sentence); | |||
| (sequence_length, _) = _pad_sequences(sequence_length, 0, max_length_sentence); | |||
| return (sequence_padded, sequence_length); | |||
| } | |||
| private static (int[][], int[]) _pad_sequences(int[][] sequences, int pad_tok, int max_length) | |||
| { | |||
| var sequence_length = new int[sequences.Length]; | |||
| for (int i = 0; i < sequences.Length; i++) | |||
| { | |||
| sequence_length[i] = sequences[i].Length; | |||
| Array.Resize(ref sequences[i], max_length); | |||
| } | |||
| return (sequences, sequence_length); | |||
| } | |||
| private static (int[][][], int[]) _pad_sequences(int[][][] sequences, int[] pad_tok, int max_length) | |||
| { | |||
| var sequence_length = new int[sequences.Length]; | |||
| for (int i = 0; i < sequences.Length; i++) | |||
| { | |||
| sequence_length[i] = sequences[i].Length; | |||
| Array.Resize(ref sequences[i], max_length); | |||
| for (int j = 0; j < max_length - sequence_length[i]; j++) | |||
| { | |||
| sequences[i][max_length - j - 1] = new int[pad_tok.Length]; | |||
| Array.Copy(pad_tok, sequences[i][max_length - j - 1], pad_tok.Length); | |||
| } | |||
| } | |||
| return (sequences, sequence_length); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,4 +1,5 @@ | |||
| using System; | |||
| using NumSharp; | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.IO; | |||
| using System.Linq; | |||
| @@ -7,6 +8,7 @@ using Tensorflow; | |||
| using Tensorflow.Estimator; | |||
| using TensorFlowNET.Examples.Utility; | |||
| using static Tensorflow.Python; | |||
| using static TensorFlowNET.Examples.DataHelpers; | |||
| namespace TensorFlowNET.Examples.Text.NER | |||
| { | |||
| @@ -27,10 +29,17 @@ namespace TensorFlowNET.Examples.Text.NER | |||
| HyperParams hp; | |||
| Dictionary<string, int> vocab_tags = new Dictionary<string, int>(); | |||
| int nwords, nchars, ntags; | |||
| CoNLLDataset dev, train; | |||
| Tensor word_ids_tensor; | |||
| Tensor sequence_lengths_tensor; | |||
| Tensor char_ids_tensor; | |||
| Tensor word_lengths_tensor; | |||
| Tensor labels_tensor; | |||
| Tensor dropout_tensor; | |||
| Tensor lr_tensor; | |||
| public bool Run() | |||
| { | |||
| PrepareData(); | |||
| @@ -38,6 +47,14 @@ namespace TensorFlowNET.Examples.Text.NER | |||
| tf.train.import_meta_graph("graph/lstm_crf_ner.meta"); | |||
| word_ids_tensor = graph.OperationByName("word_ids"); | |||
| sequence_lengths_tensor = graph.OperationByName("sequence_lengths"); | |||
| char_ids_tensor = graph.OperationByName("char_ids"); | |||
| word_lengths_tensor = graph.OperationByName("word_lengths"); | |||
| labels_tensor = graph.OperationByName("labels"); | |||
| dropout_tensor = graph.OperationByName("dropout"); | |||
| lr_tensor = graph.OperationByName("lr"); | |||
| var init = tf.global_variables_initializer(); | |||
| with(tf.Session(), sess => | |||
| @@ -47,6 +64,7 @@ namespace TensorFlowNET.Examples.Text.NER | |||
| foreach (var epoch in range(hp.epochs)) | |||
| { | |||
| print($"Epoch {epoch + 1} out of {hp.epochs}"); | |||
| run_epoch(train, dev, epoch); | |||
| } | |||
| }); | |||
| @@ -54,6 +72,77 @@ namespace TensorFlowNET.Examples.Text.NER | |||
| return true; | |||
| } | |||
| private void run_epoch(CoNLLDataset train, CoNLLDataset dev, int epoch) | |||
| { | |||
| int i = 0; | |||
| // iterate over dataset | |||
| var batches = minibatches(train, hp.batch_size); | |||
| foreach (var(words, labels) in batches) | |||
| { | |||
| get_feed_dict(words, labels, hp.lr, hp.dropout); | |||
| } | |||
| } | |||
| private IEnumerable<((int[][], int[])[], int[][])> minibatches(CoNLLDataset data, int minibatch_size) | |||
| { | |||
| var x_batch = new List<(int[][], int[])>(); | |||
| var y_batch = new List<int[]>(); | |||
| foreach(var (x, y) in data.GetItems()) | |||
| { | |||
| if (len(y_batch) == minibatch_size) | |||
| { | |||
| yield return (x_batch.ToArray(), y_batch.ToArray()); | |||
| x_batch.Clear(); | |||
| y_batch.Clear(); | |||
| } | |||
| var x3 = (x.Select(x1 => x1.Item1).ToArray(), x.Select(x2 => x2.Item2).ToArray()); | |||
| x_batch.Add(x3); | |||
| y_batch.Add(y); | |||
| } | |||
| if (len(y_batch) > 0) | |||
| yield return (x_batch.ToArray(), y_batch.ToArray()); | |||
| } | |||
| /// <summary> | |||
| /// Given some data, pad it and build a feed dictionary | |||
| /// </summary> | |||
| /// <param name="words"> | |||
| /// list of sentences. A sentence is a list of ids of a list of | |||
| /// words. A word is a list of ids | |||
| /// </param> | |||
| /// <param name="labels">list of ids</param> | |||
| /// <param name="lr">learning rate</param> | |||
| /// <param name="dropout">keep prob</param> | |||
| private FeedItem[] get_feed_dict((int[][], int[])[] words, int[][] labels, float lr = 0f, float dropout = 0f) | |||
| { | |||
| int[] sequence_lengths; | |||
| int[][] word_lengths; | |||
| int[][] word_ids; | |||
| int[][][] char_ids; | |||
| if (true) // use_chars | |||
| { | |||
| (char_ids, word_ids) = (words.Select(x => x.Item1).ToArray(), words.Select(x => x.Item2).ToArray()); | |||
| (word_ids, sequence_lengths) = pad_sequences(word_ids, pad_tok: 0); | |||
| (char_ids, word_lengths) = pad_sequences(char_ids, pad_tok: 0); | |||
| } | |||
| // build feed dictionary | |||
| var feeds = new List<FeedItem>(); | |||
| feeds.Add(new FeedItem(word_ids_tensor, np.array(word_ids))); | |||
| feeds.Add(new FeedItem(sequence_lengths_tensor, np.array(sequence_lengths))); | |||
| if(true) // use_chars | |||
| { | |||
| feeds.Add(new FeedItem(char_ids_tensor, np.array(char_ids))); | |||
| feeds.Add(new FeedItem(word_lengths_tensor, np.array(word_lengths))); | |||
| } | |||
| throw new NotImplementedException("get_feed_dict"); | |||
| } | |||
| public void PrepareData() | |||
| { | |||
| hp = new HyperParams("LstmCrfNer") | |||
| @@ -8,13 +8,14 @@ using Tensorflow.Estimator; | |||
| namespace TensorFlowNET.Examples.Utility | |||
| { | |||
| public class CoNLLDataset : IEnumerable | |||
| public class CoNLLDataset | |||
| { | |||
| static Dictionary<string, int> vocab_chars; | |||
| static Dictionary<string, int> vocab_words; | |||
| static Dictionary<string, int> vocab_tags; | |||
| List<Tuple<int[], int>> _elements; | |||
| HyperParams _hp; | |||
| string _path; | |||
| public CoNLLDataset(string path, HyperParams hp) | |||
| { | |||
| @@ -24,22 +25,10 @@ namespace TensorFlowNET.Examples.Utility | |||
| if (vocab_words == null) | |||
| vocab_words = load_vocab(hp.filepath_words); | |||
| var lines = File.ReadAllLines(path); | |||
| if (vocab_tags == null) | |||
| vocab_tags = load_vocab(hp.filepath_tags); | |||
| foreach (var l in lines) | |||
| { | |||
| string line = l.Trim(); | |||
| if (string.IsNullOrEmpty(line) || line.StartsWith("-DOCSTART-")) | |||
| { | |||
| } | |||
| else | |||
| { | |||
| var ls = line.Split(' '); | |||
| // process word | |||
| var word = processing_word(ls[0]); | |||
| } | |||
| } | |||
| _path = path; | |||
| } | |||
| private (int[], int) processing_word(string word) | |||
| @@ -58,6 +47,20 @@ namespace TensorFlowNET.Examples.Utility | |||
| return (char_ids, id); | |||
| } | |||
| private int processing_tag(string word) | |||
| { | |||
| // 1. preprocess word | |||
| if (false) // lowercase | |||
| word = word.ToLower(); | |||
| if (false) // isdigit | |||
| word = "$NUM$"; | |||
| // 2. get id of word | |||
| int id = vocab_tags.GetValueOrDefault(word, -1); | |||
| return id; | |||
| } | |||
| private Dictionary<string, int> load_vocab(string filename) | |||
| { | |||
| var dict = new Dictionary<string, int>(); | |||
| @@ -68,9 +71,38 @@ namespace TensorFlowNET.Examples.Utility | |||
| return dict; | |||
| } | |||
| public IEnumerator GetEnumerator() | |||
| public IEnumerable<((int[], int)[], int[])> GetItems() | |||
| { | |||
| return _elements.GetEnumerator(); | |||
| var lines = File.ReadAllLines(_path); | |||
| int niter = 0; | |||
| var words = new List<(int[], int)>(); | |||
| var tags = new List<int>(); | |||
| foreach (var l in lines) | |||
| { | |||
| string line = l.Trim(); | |||
| if (string.IsNullOrEmpty(line) || line.StartsWith("-DOCSTART-")) | |||
| { | |||
| if (words.Count > 0) | |||
| { | |||
| niter++; | |||
| yield return (words.ToArray(), tags.ToArray()); | |||
| words.Clear(); | |||
| tags.Clear(); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| var ls = line.Split(' '); | |||
| // process word | |||
| var word = processing_word(ls[0]); | |||
| var tag = processing_tag(ls[1]); | |||
| words.Add(word); | |||
| tags.Add(tag); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||