-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix issue 4322, enable lda summary output #5260
Conversation
Awesome! This was a much asked feature, it's great that you've fixed this 😄 I always assumed that our implementation of Lda held the topics in a compressed hard-to-read manner, and I wasn't aware that "GetLdaDetails()" returned a readable version of the topics already! Good to know 👍 |
public ModelParameters GetLdaDetails(int topicIndex) | ||
{ | ||
Contracts.Assert(0 <= iinfo && iinfo < _ldas.Length); | ||
Contracts.Assert(0 <= topicIndex && topicIndex < _ldas.Length); | ||
|
||
var ldaState = _ldas[iinfo]; | ||
var mapping = _columnMappings[iinfo]; | ||
var ldaState = _ldas[topicIndex]; | ||
var mapping = _columnMappings[topicIndex]; | ||
|
||
return ldaState.GetLdaSummary(mapping); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The way I understand it GetLdaDetails(i)
will give you the LdaSummary for column i
not the topic i
(because each column will have several topics). So I think that renaming iinfo
to topicIndex
and the documentation you added is somewhat misguiding. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right! I confused this with topic index, thanks
In reply to: 445984832 [](ancestors = 445984832)
Codecov Report
@@ Coverage Diff @@
## master #5260 +/- ##
==========================================
- Coverage 73.62% 73.50% -0.13%
==========================================
Files 1022 1014 -8
Lines 190056 188738 -1318
Branches 20444 20340 -104
==========================================
- Hits 139930 138732 -1198
+ Misses 44595 44485 -110
+ Partials 5531 5521 -10
|
{ | ||
var summary = GetLdaDetails(i); | ||
|
||
ctx.SaveTextStream(WordTopicModelFilename, writer => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know that the WordTopicModelFilename
was unused in the codebase before your changes on this PR, and it was a legacy variable coming from TLC (as commented here), but I'm not sure this is the best way to use it.
The potential problem I see is that for every _columns[i] that we save, we'll call this method (if _columns[i].OutputTopicWordSummary == true, as seen in your code above), and all of them will try to save their WordSummary to the same WordTopicModelFilename
.
So if a given LdaTransformer has more than 1 column with OutputTopicWordSummary
enabled, then 2 or more columns will try to output to the same file.
Looking at the implementation of ctx.SaveTextStream
I'm not sure if it will throw if we try to write twice the same file, or if it will overwrite them. If it does, this would be a problem. If it doesn't throw, there's still the problem that we will have the output topics of all the columns in one single file, and no way to know which file is of which column.
So adding text to identify where the output of one column begins would be useful. Another sugestion would be to actually use one file per column (for example, something like ""word_topic_summary-col{i}.txt" might be sufficient.
(PS: I don't know what was the behavior back on TLC, but maybe there we couldn't have 1 Lda transformer acting on several columns). #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sounds good, I will add column option name (which is output column name from options) as part of summary text file
In reply to: 445988583 [](ancestors = 445988583)
{ | ||
foreach (var wordScore in wordScores) | ||
{ | ||
writer.WriteLine($"Top[{topId}]: {wordScore.Word}\t{wordScore.Score}"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I think it might be clearer to use $"Topic[{topId}]
since "Top" might be somewhat misguiding. The same applies below. #Resolved
@@ -141,6 +141,9 @@ internal sealed class Column : OneToOneColumn | |||
[Argument(ArgumentType.AtMostOnce, HelpText = "Reset the random number generator for each document", ShortName = "reset")] | |||
public bool? ResetRandomGenerator; | |||
|
|||
[Argument(ArgumentType.AtMostOnce, HelpText = "Whether to output the topic-word summary in text format", ShortName = "summary")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Suggestion, change the HelpText of this argument and the one on internal Options.OutputTopicWordSummary
to be "Whether to output the topic-word summary in text format when saving the model to disk"`, else it isn't understood that users will actually need to save the model to disk in order to get that output. (see microsoft/NimbusML#405 where the user actually expected to see the output on the console).
Also update the docs elsewhere where you've added this option. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
// topic summary text file saved inside the model.zip file. | ||
string name = TestName + ".zip"; | ||
string modelPath = GetOutputPath("SavePipe", name); | ||
using (var file = Env.OpenInputFile(modelPath)) | ||
using (var strm = file.OpenReadStream()) | ||
using (var zip = new ZipArchive(strm, ZipArchiveMode.Read)) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(appended to code to allow for threaded comment)
Can you please also add a test to test your PR on ML.NET's API instead of MAML?
I think modifying a test such as InspectLdaModelParameters would be enough. Notice that in particular that test was waiting for this issue to be fixed in order to test the output summary:
machinelearning/test/Microsoft.ML.Functional.Tests/IntrospectiveTraining.cs
Lines 190 to 191 in 45a16dc
// Get the trained LDA model. | |
// TODO #2197: Get the topics and summaries from the model. |
Thanks.! #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great idea, I will test both MAML and from transformer. Actually test from MAML and check summary was how TLC originally test topic summary.
In reply to: 446344620 [](ancestors = 446344620)
.Append(mlContext.Transforms.Text.LatentDirichletAllocation("Features", "SentimentBag", | ||
numberOfTopics: numTopics, maximumNumberOfIterations: 10)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(appended to code to allow for threaded discussion)
Should we also add the outputWordSummary
option to the ML.NET public API found here on the TextCatalog?:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That will be break public API, I think we should avoid break public API as much as possible. User still can get summary via the public interface "GetLdaDetails" from lda transformer as this test shows
In reply to: 448136744 [](ancestors = 448136744)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. I don't think it's a good idea to add this option to this public API. No need for a new API only for this.
In reply to: 448522335 [](ancestors = 448522335,448512997,448136744)
[BestFriend] | ||
internal ModelParameters GetLdaDetails(int iinfo) | ||
/// <summary> | ||
/// Method to provide details about the topic discovered by LightLDA |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: "about the topics discovered by LightLDA" #Resolved
/// <param name="columnIndex">index of column</param> | ||
/// <returns></returns> | ||
public ModelParameters GetLdaDetails(int columnIndex) | ||
{ | ||
Contracts.Assert(0 <= iinfo && iinfo < _ldas.Length); | ||
Contracts.Assert(0 <= columnIndex && columnIndex < _ldas.Length); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: So I don't know how usable this API would be for users, specially because users might not be sure of what index corresponds to the column they want. Is it possible to have public ModelParameters GetLdaDetails(string columnName)
instead? (throw an exception if the provided name can't be matched with the names found in _columns) #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The order is same order as user define column options pair in below API. In most cases this should be 0 as user is using lda by input column, output column pair.
Maybe I can rename this as "index of column options pair" to make it clear
/// <include file='doc.xml' path='doc/members/member[@name="LightLDA"]/*' />
/// <param name="env">The environment.</param>
/// <param name="columns">Describes the parameters of the LDA process for each column pair.</param>
internal LatentDirichletAllocationEstimator(IHostEnvironment env, params ColumnOptions[] columns)
{
Contracts.CheckValue(env, nameof(env));
_host = env.Register(nameof(LatentDirichletAllocationEstimator));
_columns = columns.ToImmutableArray();
}
In reply to: 448138812 [](ancestors = 448138812)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. I think rephrasing the docs of GetLdaDetails
to be "Index of the column options pair" would be better.
In reply to: 448520738 [](ancestors = 448520738,448138812)
Hi everyone, great to have topic summary available. And it IS available directly after training. BUT: Where can "OutputTopicWordSummary" actually be set to true to trigger saving the details together with the model? Probably I'm missing something, I'm not really firm with the framework yet. However:
Thanks |
fix issue 4322: #4322