Draft: GPT2 training on MCQ med data#1111
Conversation
fd4b676 to
3014cf5
Compare
…k for model to contxt 512
JulienVig
left a comment
There was a problem hiding this comment.
1st part of my review, I'll finish at a different time, feel free to start addressing these comments
| /simple_face-example.png | ||
| /titanic* | ||
| /mnist* | ||
| /medicalMCQtxtNoExplanation |
| roundIterations?: number; | ||
| batchSize: number; | ||
| validationSplit: number; | ||
| validationFrequency?: number; | ||
| datasetPath?: string; | ||
| validationDatasetPath?: string; |
There was a problem hiding this comment.
Can you document how all the validation parameters interact with each others? For example what happens if you specify both validationSplit and validationDatasetPath?
| ) | ||
| throw new Error("validationFrequency must be a non-negative integer"); | ||
|
|
||
| // if (!Number.isInteger(this.#epochs / this.#roundDuration)) |
There was a problem hiding this comment.
| // if (!Number.isInteger(this.#epochs / this.#roundDuration)) |
| debugProcessMemory( | ||
| `[${shortenId(this.ownId)}] round ${round} before encode`, | ||
| ); | ||
| const payload = await serialization.weights.encode(payloadToServer); | ||
| debugProcessMemory( | ||
| `[${shortenId(this.ownId)}] round ${round} after encode`, |
There was a problem hiding this comment.
is it useful to log the before after?
| id: NodeID; | ||
| waitForMoreParticipants: boolean; | ||
| payload: serialization.Encoded; | ||
| payload?: serialization.Encoded | null; |
| ? this.#runRounds(dataset, validationDataset) | ||
| : this.#runIterationRounds(dataset, validationDataset); |
There was a problem hiding this comment.
I think these method names are not explicit enough, what about #runRoundsByEpoch and #runRoundsByIteration? Can you add documentation for each method to clearly differentiate their intended use and behaviors?
There was a problem hiding this comment.
Same comment for runRound and runIterationRound
| let pendingBatch: Batched<DataFormat.ModelEncoded[D]> | undefined = | ||
| next.done === true ? undefined : next.value; | ||
|
|
||
| while (pendingBatch !== undefined) { |
There was a problem hiding this comment.
| let pendingBatch: Batched<DataFormat.ModelEncoded[D]> | undefined = | |
| next.done === true ? undefined : next.value; | |
| while (pendingBatch !== undefined) { | |
| while (next.done !== true) { |
I think you can simplify the implementation and drop pendingBatch if you directly use next.done and next.value instead
| const [gen, result] = async_iterator.split( | ||
| model.trainNextBatches( | ||
| datasetIterator as AsyncIterator< | ||
| Batched<DataFormat.ModelEncoded["text"]> | ||
| >, | ||
| maxBatchCount, | ||
| validationDataset as | ||
| | Dataset<Batched<DataFormat.ModelEncoded["text"]>> | ||
| | undefined, | ||
| setDone, | ||
| ), | ||
| ); | ||
|
|
||
| yield gen; | ||
| const epochLogs = await result; | ||
| epochsLogs = epochsLogs.push(epochLogs); |
There was a problem hiding this comment.
| const [gen, result] = async_iterator.split( | |
| model.trainNextBatches( | |
| datasetIterator as AsyncIterator< | |
| Batched<DataFormat.ModelEncoded["text"]> | |
| >, | |
| maxBatchCount, | |
| validationDataset as | |
| | Dataset<Batched<DataFormat.ModelEncoded["text"]>> | |
| | undefined, | |
| setDone, | |
| ), | |
| ); | |
| yield gen; | |
| const epochLogs = await result; | |
| epochsLogs = epochsLogs.push(epochLogs); | |
| const [gen, epochLogs] = async_iterator.split( | |
| model.trainNextBatches( | |
| datasetIterator as AsyncIterator< | |
| Batched<DataFormat.ModelEncoded["text"]> | |
| >, | |
| maxBatchCount, | |
| validationDataset as | |
| | Dataset<Batched<DataFormat.ModelEncoded["text"]>> | |
| | undefined, | |
| setDone, | |
| ), | |
| ); | |
| yield gen; | |
| epochsLogs = epochsLogs.push(await epochLogs); |
| #hashTokenContext(tokens: number[]): number { | ||
| let hash = 0x811c9dc5; | ||
|
|
||
| for (const token of tokens) { | ||
| hash ^= token & 0xff; | ||
| hash = Math.imul(hash, 0x01000193); | ||
| hash ^= (token >>> 8) & 0xff; | ||
| hash = Math.imul(hash, 0x01000193); | ||
| hash ^= (token >>> 16) & 0xff; | ||
| hash = Math.imul(hash, 0x01000193); | ||
| hash ^= (token >>> 24) & 0xff; | ||
| hash = Math.imul(hash, 0x01000193); | ||
| hash ^= 0xff; | ||
| hash = Math.imul(hash, 0x01000193); | ||
| } | ||
|
|
||
| return hash >>> 0; | ||
| } | ||
| } |
There was a problem hiding this comment.
Can you add documentation to this method?
| const model = this.model as unknown as IterationTrainableTextModel; | ||
| if (typeof model.trainNextBatches !== "function") | ||
| throw new Error("model does not support iteration-based training"); | ||
|
|
There was a problem hiding this comment.
I find this part really inconvenient because it's not generic, it only addresses on particular case. Here's an idea to work around this, let me know what you think
- Implement a new dataset method that splits the dataset after a specified number of steps and returns 1) one dataset with only the next specified number of batches and 2) the remainder of the dataset after these batches. e.g., const [nextBatches, datasetRemainder] = dataset.subset(maxBatchCount)
- From runIterationRounds, I think this allows you to do reuse the existing #runRound, e.g. this.#runRound(nextBatches, roundValidationDataset)
- With this method I don't think you need to deal with all the iterators
Let me know what you think and whether this is feasible
No description provided.