Roman Parker
Intern, Wolfram Research
Teacher forcing networks are extremely effective for generating text, especially if that text contains specific repeating patterns. This makes it ideal for creating text based on Magic: The Gathering cards, which contain several easily-distinguishable patterns. Since there are over 20,000 different cards, there is a significant amount of data for the network to train on. In addition, the Japanese poem style of Haiku has a specific short form. This is good for analysis by this AI, demonstrating that it works on different kinds of text.
Magic: The Gathering Card Generation
Magic: The Gathering Card Generation
In this section, a teacher forcing network is created to generate the text of the Magic: The Gathering cards. All cards have the following five parts, in this order:
◼
Card name (what the card is called)
◼
Card type (basic information about when the card can be used and what rules it follows)
◼
Mana cost (how easy is it to play the card)
◼
Rules text (what the card does)
◼
Set symbol (when the card was printed)
This makes it relatively easy for an AI to create cards that follow this basic pattern. Cards also often share specific words in their rules text which instruct the player on how to treat the cards, such as "flying" or "vigilance," which makes it easier for the network to create these cards.
For this program, a teacher forcing network will be defined with two GatedRecurrentLayers as the main part of the network. Unlike most networks for text training, which take an input of characters and try to predict the next character, a teacher forcing network takes the input of characters and tries to predict the string of characters of the same length of the input, starting from the second character of the input. For example, if the input is "I like to code in the Wolfram Languag" and the "correct" character is "e," the network will attempt to generate " like to code in the Wolfram Language," rather than just the "e." This helps the network understand the context of the character better than if it simply had to generate a single character.
Import the plaintext of all Magic: The Gathering cards printed up to the year 2018:
In[]:=
corpus=;
To create the training data, a “sliding window” of length 500 is created. Starting from the first character of the corpus, the first 500 characters become the input portion of the first piece of training data, and the 501st character is the output. Then, for the next piece of data, the 2nd through 501st character are the input and the 502nd is the output. This process is done until the end of the dataset is reached, creating the training data. This works nicely with the teacher forcing networks since every piece of training data is identical to the “correct answer” of the previous piece of training data.
Create the training data:
In[]:=
trainingData=Module[{b=Characters[corpus]},Thread[Table[StringJoin[b[[i;;i+500]]]b[[i+501]],{i,Length[b]-501}]]];
Display a random data point in the training data:
In[]:=
trainingData[[RandomInteger[{1,Length[trainingData]}]]]
Out[]=
ntil end of turn.US-CRavenous Trap2BBInstant -- TrapIf an opponent had three or more cards put into their graveyard from anywhere this turn, you may pay {0} rather than pay this spell's mana cost.Exile all cards from target player's graveyard.ZEN-URavenous Vampire3BBCreature -- Vampire3/3FlyingAt the beginning of your upkeep, you may sacrifice a nonartifact creature. If you do, put a +1/+1 counter on Ravenous Vampire. If you don't, tap Ravenous Vampire.MI-URaving Dead4BCreature
Find all characters used in the dataset:
In[]:=
characters=Union[Flatten[Characters[corpus]]]
Out[]=
!,&,*,(,),_,-,+,{,[,},],.,,,;,",?,',/,:,, ,,,,²,½,0,1,2,3,4,5,6,7,8,9,a,â,A,Â,b,B,c,C,d,D,e,E,f,F,g,G,h,H,i,I,Ï,j,J,k,K,l,L,m,M,n,N,o,O,p,P,q,Q,r,R,s,S,t,T,u,U,v,V,w,W,x,X,y,Y,z,Z,¢
Find length of card list:
In[]:=
n=Length[characters];
Define the NetChain of the network:
In[]:=
predict=NetChain[{UnitVectorLayer[n],GatedRecurrentLayer[128],GatedRecurrentLayer[128],NetMapOperator[LinearLayer[n]],SoftmaxLayer[]}]
Out[]=
NetChain
Create the teacher forcing network based on the example in tutorial/NeuralNetworksSequenceLearning:
In[]:=
teacherForcingNet=NetGraph[<|"predict"predict,"rest"SequenceRestLayer[],"most"SequenceMostLayer[],"loss"CrossEntropyLossLayer["Index"]|>,{NetPort["Input"]"most""predict"NetPort["loss","Input"],NetPort["Input"]"rest"NetPort["loss","Target"]},"Input"NetEncoder[{"Characters",characters}]]
Out[]=
NetGraph
Train the network (takes a long time):
result=NetTrain[teacherForcingNet,<|"Input"Keys[trainingData],"Output"Values[trainingData]|>,All,ValidationSetScaled[0.1],TargetDevice"GPU",TrainingStoppingCriterion"Loss",MaxTrainingRounds3]
Extract network:
In[]:=
trained=NetExtract[result["TrainedNet"],"predict"]
Out[]=
NetChain
Save a prediction net for generating text and potential future training:
In[]:=
predictNet=NetJoin[NetTake[trained,3,"Input"Automatic],{SequenceLastLayer[],NetExtract[trained,{4,"Net"}],SoftmaxLayer[]},"Input"NetEncoder[{"Characters",characters}],"Output"NetDecoder[{"Class",characters}]]
Out[]=
NetChain
Define code to generate text and generate example text:
In[]:=
generateSample[net_,start_,len_]:=Block[{obj=NetStateObject[net]},StringJoin@NestList[obj[#,"RandomSample"]&,start,len]]
In[]:=
generate[net_,start_,len_]:=StringJoin@NestList[NetStateObject[net],start,len]
Generate Magic: The Gathering cards from the network:
In[]:=
generateSample[predictNet,"",500]
Out[]=
{1}{W}, {T}: Discard a card." (Tap X is your control since the beginning of remove attach opponent.)Phaebling Myr Responephere enters the battlefield with three ores.AKH-CGruul GangLandYou may have Jace's Shambler gets +1/+0 until end of turn.WWK-CDrown Loby1WEnchantment -- AuraEnchant creatureEnchanted creature has "{2}{U}, {T}: Put a +1/+1 counter on this creature.)M15-ULifeblood Totem2UUCreature tokehs without flying and If an opponent controls a land of each basic thir comb
A few of the interesting cards the network created after training for about 24 hours:
◼
Regenge Myr
4
Artifact Creature -- Scarecrow
1/4
{2}, Pay 1 life: Creatures you control get +2/+2 until end of turn.
EMN-U
4
Artifact Creature -- Scarecrow
1/4
{2}, Pay 1 life: Creatures you control get +2/+2 until end of turn.
EMN-U
◼
Earth Squire
2
Artifact
Tap two untapped permanents you control: Destroy target creature.
LGN-U
2
Artifact
Tap two untapped permanents you control: Destroy target creature.
LGN-U
◼
Pursi
4W
Instant
Creatures you control get +1/+1 and gain lifelink until end of turn.
DOM-U
4W
Instant
Creatures you control get +1/+1 and gain lifelink until end of turn.
DOM-U
◼
Descack Mamchais
4BBGG
Legendary Artifact
{3}{B}, {T}: Put a creature card from a graveyard onto the battlefield with three +1/+1 counters on it.
RTR-M
4BBGG
Legendary Artifact
{3}{B}, {T}: Put a creature card from a graveyard onto the battlefield with three +1/+1 counters on it.
RTR-M
◼
Llaitic Sergecling
1U
Creature -- Bird
2/2
Flying
Whenever a creature deals damage to you, draw a card.
ISD-R
1U
Creature -- Bird
2/2
Flying
Whenever a creature deals damage to you, draw a card.
ISD-R
◼
Workspiant Archer
3W
Creature -- Griffin
1/4
Flying
Pay 2 life: Regenerate target creature.
HOU-C
3W
Creature -- Griffin
1/4
Flying
Pay 2 life: Regenerate target creature.
HOU-C
◼
Spire Cull
2BB
Enchantment
White creatures you control get +1/+1.
LE-U
2BB
Enchantment
White creatures you control get +1/+1.
LE-U
◼
Smolderous Farm
2GG
Enchantment
Whenever you cast a spell, you may draw a card.
MRD-U
2GG
Enchantment
Whenever you cast a spell, you may draw a card.
MRD-U
◼
Honee Temple
Land
{T}: Add {C}.
{T}: You may prevent all damage that would be dealt to target permanent you control this turn. If you do, draw a card.
IN-R
Land
{T}: Add {C}.
{T}: You may prevent all damage that would be dealt to target permanent you control this turn. If you do, draw a card.
IN-R
◼
Ojutai Distorvo
G
Enchantment
Sacrifice a Mountain: Creatures you control gain indestructible until end of turn.
RAV-U, C13-U
G
Enchantment
Sacrifice a Mountain: Creatures you control gain indestructible until end of turn.
RAV-U, C13-U
◼
Parading Stronghold
2W
Creature -- Elf
2/1
As an additional cost to cast this spell, sacrifice a creature.
When this creature enters the battlefield, you gain 10 life.
Morph {5}{G}{G} (You may cast this card face down as a 2/2 creature for {3}. Turn it face up any time for its morph cost.)
ONS-R
2W
Creature -- Elf
2/1
As an additional cost to cast this spell, sacrifice a creature.
When this creature enters the battlefield, you gain 10 life.
Morph {5}{G}{G} (You may cast this card face down as a 2/2 creature for {3}. Turn it face up any time for its morph cost.)
ONS-R
As can be seen in the 500 characters of generated cards above, many of the cards don’t make much sense or contain gibberish. However, almost all of the cards follow the correct textual patterns, demonstrating that the teacher forcing network was able to pick up on the five parts. Additionally, approximately 10% of the cards, besides their unusual names, make perfect sense and seem to be human-designed. This shows that the program is capable of generating text with a well-defined pattern and structure to it. The same network is next used to generate Haiku, which has a less obvious pattern in its structure.
Haiku Data
Haiku Data
Haiku is a Japanese short poem form with no rhyme or stress requirements, but the first and third lines have only five syllables, while the second has seven. Due to the strict syllabic requirements and short length, the poems are often difficult to write. A challenge this poses for a teacher forcing network is that, in English, there is no obvious divider between syllables in text, so it should be difficult for the program to create technically correct poems. Additionally, the varied topics in this dataset mean that there are few semantic patterns for the AI to pick up on to create grammatically and linguistically cohesive text.
Import the data:
Find the characters used:
Like on the previous dataset, the training data input uses a sliding window, but the “window” size is only 70 characters instead of 200. This is approximately the mean length of the poems.
Create the training data from all possible continuous length-70 strings in the corpus:
Find how many characters are in the haiku dataset:
Create the NetChain:
Define the network:
Train the network:
Extract the network:
Save the prediction net:
Generate haiku:
A few of the interesting poems this program created:
I had given up
before you been on account
recreated will
There’s nothing better
and we not crush up in Memes
are written boogies
Please have your very
current trust my line
a brand of umbrellas
I never wanted
crossed spring words to the same
long place is that bless
I just gotta grow
your sentence finishes times
I already know
I think I wonder
now and be called time after
that this is some type
I don’t even know
why you can do the husbands
oh meeting and straight
I had given up
before you been on account
recreated will
There’s nothing better
and we not crush up in Memes
are written boogies
Please have your very
current trust my line
a brand of umbrellas
I never wanted
crossed spring words to the same
long place is that bless
I just gotta grow
your sentence finishes times
I already know
I think I wonder
now and be called time after
that this is some type
I don’t even know
why you can do the husbands
oh meeting and straight
Concluding remarks
Concluding remarks
Impressively, this AI is quite adept at creating haiku which follow the 5-7-5 syllable pattern, even without actually knowing where the syllable breaks are located. However, the poems often lack a narrative structure or flow, suggesting that either the AI didn’t train enough to pick it up or the haiku dataset contained too many different topics for it to figure out what theme to follow in each haiku.
This demonstrates that it is possible to use the same AI, only modifying the training of the network, to create two very different types of text. It is likely possible to model any kind of text consisting of many repeated patterns using this network, making it extremely useful.
Acknowledgment
Acknowledgment
I’d like to thank Siria Sadeddin (WRI) and Mads Bahrami (WRI) for their help in improving the code and the essay.
