Jawa
Nowa edycja : Jeszcze więcej poprawek w wolnym czasie. Uruchomiłem nową gałąź, w której bawiłem się algorytmem DFS. Oficjalnie gałąź ma działać jako rdzeń nowego algorytmu BFS, który planuję, ale tymczasem chciałem lepiej zrozumieć, co robi DFS i jak podejmuje decyzje. W tym celu dodałem funkcję tłumienia, która zaczyna zanikać wartość nowego słowa, bez względu na temat czy nie, w miarę jak zdania stają się dłuższe. Wszystkie słowa wnoszą teraz wartość do zdania, ale słowa, których nie ma na liście tematów lub tematów, stanowią zaledwie 25% ich częstotliwości. Przykładową rozmowę można znaleźć tutaj i jest całkiem dobra, gdzie rozmawiamy o fizyce, ludzkiej naturze Chatbrains i innych fascynujących tematach.kod oddziału tutaj .
Edycja : Trochę poprawiałem kod. Zamiast publikować tutaj wersje, sprawdź je w moim repozytorium github, gdzie znajdziesz najnowsze wersje. Dodałem także nową rozmowę z najnowszą wersją, w której omawiamy chatboty, pierwsze wyszukiwanie w głębokości oraz sposób programowania do budowania żywych stworzeń!
Postanowiłem przyjąć to wyzwanie całościowo. Mój chatbot zna bardzo niewiele rzeczy, od których się zaczyna - bez słów, bez składni, bez niczego. Wie, jak parsować standardowy angielski na słowa i jak rozpoznać znaki niebędące znakami interpunkcyjnymi. Otóż to. Wszystko, co wie, uczy się na podstawie interakcji z użytkownikiem. Kiedy wchodzisz w interakcję, zwraca uwagę na związki między słowami i konstruuje zdania na podstawie tych informacji. Oczywiście odwołaj się do źródła, aby uzyskać więcej informacji. Znacząco przekroczyłem zalecane oczekiwania dotyczące długości tego wyzwania, ale to dobry cel. Oto kilka najważniejszych elementów programu:
- Chatbot zaczyna się bez wiedzy (zgodnie z „Regułami”: 3 )
- Częstotliwość występowania słowa jest śledzona
- Częstotliwości słów są „zanikane”, aby konwersacja mogła przechodzić od tematu do tematu (zgodnie z „Bonusem”: 3 i 4 )
- Układ słów w obserwowanych zdaniach jest rejestrowany, więc „fraza” jest domyślnie śledzona (np. Jeśli używasz wielu wyrażeń przyimkowych podczas rozmowy z botem, bot również z nich skorzysta!)
- Zdania są budowane przez preferowanie najczęściej obserwowanych połączeń między słowami, z przypadkowymi czynnikami, które wprowadzają zmienność
- Algorytm konstruowania zdań to Głębokie Pierwsze Wyszukiwanie, które próbuje zmaksymalizować występowanie słów tematycznych w zdaniu wyjściowym, z niewielką preferencją dla zdań końcowych (zgodnie z „Premią”: 1 - Używam dość cholernie fajnego algorytmu uczenia się, który zmienia się w czasie i zachowuje wiedzę o zebranych połączeniach słów)
- edycja: Słowa tematyczne są teraz czerpane zarówno z globalnej wiedzy o powtarzających się słowach, jak iz ostatniego zdania
- edycja: Wagi słów są teraz obliczane przy użyciu podstawy logarytmicznej 4 długości słowa, więc dłuższe słowa są ważone mocniej, a krótsze słowa, słabiej - ma to zrekompensować brak prawdziwego korpusu do użycia zarówno w ważeniu, jak i eliminując słowa o wysokiej częstotliwości i niskiej wartości, co można łatwo zrobić z korpusem.
- edycja: Gdy długość zdania rośnie podczas budowy, funkcja tłumienia zaczyna zmniejszać wartość dodatkowych słów.
- edit: „Zakończenie” zdania jest teraz mniej wartościowe, ponieważ powodowało przewagę krótkich głupich zdań.
- edycja: Wszystkie słowa wnoszą teraz wartość, chociaż słowa nie na temat stanowią jedynie 25% globalnej wartości częstotliwości.
- Istnieje wbudowane maksimum głębokości, aby zapobiec zbyt dużemu zapętleniu i zbyt dużej ilości czasu spędzonego z powodu użycia słowa precedens do zbudowania zdania
- Pętle są wykrywane bezpośrednio podczas budowania zdania i chociaż są one technicznie dozwolone, istnieje duża szansa na uniknięcie pętli
- Limit czasu dostrajania służy zarówno do przycinania gałęzi, jak i finalizacji wyciągów, a także do zapobiegania przekroczeniu 5-10 sekund „dopuszczalnego opóźnienia” w regułach
Podsumowując moje połączenie z zasadami:
- Dla „Reguł”: 1 wybrałem Javę, która jest pełna, więc bądź łagodny.
- W przypadku „Reguł”: 2 , wprowadzane są same dane wejściowe użytkownika, chociaż mam kod pośredniczący, aby dodać zapisywanie / ładowanie mózgu na przyszłość
- W przypadku „Reguł”: 3 absolutnie nie ma ustalonego słownictwa. ChatBot wie, jak analizować angielski, ale to wszystko. Zaczynając, absolutnie nic nie wie.
- W przypadku „Obowiązkowych kryteriów”: 1 mój program jest dłuższy, ale zawiera wiele niesamowitych elementów. Mam nadzieję, że przeoczysz.
- W przypadku „kryteriów obowiązkowych”: 2 mam limit czasu w algorytmie konstruowania zdań, aby jednoznacznie zapobiec przeszukiwaniu dłuższemu niż 5-6 sekund. Najlepsze jak dotąd zdanie jest zwracane po upływie limitu czasu.
- W przypadku „Obowiązkowych kryteriów”: 3 tematy generalnie utrwalają się w około 10 zdaniach, więc Bot będzie do tego czasu na temat, a po 20 zdaniach będzie odpowiadał na stwierdzenia za pomocą fascynujących losowych konstrukcji, które faktycznie mają trochę sensu.
- W przypadku „Obowiązkowych kryteriów”: 4 nic nie pożyczyłem z kodu referencyjnego. Jest to całkowicie unikalna konstrukcja.
- W przypadku „Bonusu”: 1 , lubię myśleć, że ten bot jest wyjątkowy. Nie będzie tak przekonujący jak skrypty botów, ale nie ma absolutnie żadnych ograniczeń na tematy i będzie z wdziękiem (z wytrwałością) przechodził od tematu do tematu.
- Dla „Bonusu”: 2 , jest to ściśle runda, więc nie ma tu bonusu. Jeszcze. W moim algorytmie nie ma wymogu odpowiedzi, więc planuję wersję wątkową, która zajmie się tą premią.
- W przypadku „Bonus”: 3 , początkowo ten bot będzie naśladować, ale w miarę jak rozmowa będzie przebiegać poza kilkoma pierwszymi zdaniami, naśladowanie wyraźnie się zakończy.
- W przypadku „Bonus”: 4 „nastroje” nie są przetwarzane w żaden znaczący sposób, ale wraz z kolejnymi tematami preferencji bota zmieni nastroje.
- W przypadku „Bonusu”: 5 zapisywanie i ładowanie mózgu nie jest obecnie dostępne.
Tak więc spełniłem wszystkie podstawowe zasady, wszystkie obowiązkowe zasady i tymczasowo reguły premiowe 1, 3 i 4.
Jako kolejny bonus skomentowałem cały kod, więc możesz pożyczyć lub wydać zalecenia dotyczące ulepszeń. Oczywiście, ponieważ nie mam wbudowanego dialogu i wiedzy „strukturalnej”, rozmowy będą dziwne dłużej niż inne boty, ale myślę, że całkiem dobrze przestrzegam zasad.
Teraz przejdźmy do kodu (niektóre komentarze zostały zredagowane w celu dopasowania do limitu ciała) lub postępuj zgodnie z nim w GitHub, ponieważ nadal go ulepszam :
import java.util.*;
import java.util.regex.*;
public class LearningChatbot {
/**
* Static definition of final word in a statement. It never has
* any descendents, and concludes all statements. This is the only
* "starting knowledge" granted the bot.
*/
public static final ChatWord ENDWORD = new ChatWord("\n");
/**
* The Brain of this operation.
*/
private ChatbotBrain brain;
/**
* Starts LearningChatbot with a new brain
*/
public LearningChatbot() {
brain = new ChatbotBrain();
}
/**
* Starts LearningChatbot with restored brain.
*/
public LearningChatbot(String filename) {
throw new UnsupportedOperationException("Not yet implemented");
}
/**
* Invocation method.
*/
public void beginConversation() {
ChatbotBrain cb = new ChatbotBrain();
Scanner dialog = new Scanner(System.in);
boolean more = true;
while (more) {
System.out.print(" You? ");
String input = dialog.nextLine();
if (input.equals("++done")) {
System.exit(0);
} else if (input.equals("++save")) {
System.out.println("Saving not yet implemented, sorry!");
System.exit(0);
} else if (input.equals("++help")) {
getHelp();
}else {
cb.decay();
cb.digestSentence(input);
}
System.out.print("Chatbot? ");
System.out.println(cb.buildSentence());
}
}
/**
* Help display
*/
public static void getHelp() {
System.out.println("At any time during the conversation, type");
System.out.println(" ++done");
System.out.println("to exit without saving.");
System.out.println("Or type");
System.out.println(" ++save");
System.out.println("to exit and save the brain.");
System.out.println();
}
/**
* Get things started.
*/
public static void main(String[] args) {
System.out.println("Welcome to the Learning Chatbot");
System.out.println();
getHelp();
LearningChatbot lc = null;
if (args.length > 0) {
System.out.printf("Using %s as brain file, if possible.", args[0]);
lc = new LearningChatbot(args[0]);
} else {
lc = new LearningChatbot();
}
lc.beginConversation();
}
/**
* The ChatbotBrain holds references to all ChatWords and has various
* methods to decompose and reconstruct sentences.
*/
static class ChatbotBrain {
/**
* A tracking of all observed words. Keyed by the String version of
* the ChatWord, to allow uniqueness across all ChatWords
*/
private Map<String,ChatWord> observedWords;
/**
* This brain is going to be able to keep track of "topics" by way of
* a word frequency map. That way, it can generate sentences based
* on topic-appropriateness.
*/
private Map<ChatWord, Double> wordFrequencyLookup;
/**
* This holds the actual word frequencies, for quick isolation of
* highest frequency words.
*/
private NavigableMap<Double, Collection<ChatWord>> wordFrequency;
/**
* This holds the count of words observed total.
*/
private int wordCount;
/**
* This holds the current "values" of all words.
*/
private double wordValues;
/**
* A "word" that is arbitrarily the start of every sentence
*/
private ChatWord startWord;
/**
* Rate of decay of "topics".
*/
private double decayRate;
// These values configure various features of the recursive
// sentence construction algorithm.
/** Nominal (target) length of sentences */
public static final int NOMINAL_LENGTH = 10;
/** Max length of sentences */
public static final int MAX_LENGTH = 25;
/** Sentence creation timeout */
public static final long TIMEOUT = 5000;
/** Topic words to match against */
public static final int TOPICS = 3;
/** Minimum branches to consider for each word */
public static final int MIN_BRANCHES = 3;
/** Maximum branches to consider for each word */
public static final int MAX_BRANCHES = 5;
/** % chance as integer out of 100 to skip a word */
public static final int SKIP_CHANCE = 20;
/** % chance as integer to skip a word that would cause a loop */
public static final int LOOP_CHANCE = 5;
/** % chance that punctuation will happen at all */
public static final int PUNCTUATION_CHANCE = 25;
/** % chance that a particular punctuation will be skipped */
public static final int PUNCTUATION_SKIP_CHANCE = 40;
/**
* Convenience parameter to use a common random source
* throughout the brain.
*/
private Random random;
/**
* Gets the Chatbot started, sets up data structures necessary
*/
public ChatbotBrain() {
observedWords = new HashMap<String,ChatWord>();
observedWords.put("\n",ENDWORD);
startWord = new ChatWord("");
observedWords.put("",startWord);
wordFrequencyLookup = new HashMap<ChatWord, Double>();
wordFrequency = new TreeMap<Double, Collection<ChatWord>>();
decayRate = 0.05;
wordCount = 0;
wordValues = 0.0;
random = new Random();
}
/**
* More complex digest method (second edition) that takes a sentence,
* cuts it pu, and links up the words based on ordering.
*/
public void digestSentence(String sentence) {
Scanner scan = new Scanner(sentence);
ChatWord prior = null;
ChatWord current = null;
String currentStr = null;
String currentPnc = null;
while (scan.hasNext()) {
currentStr = scan.next();
Pattern wordAndPunctuation =
Pattern.compile("([a-zA-Z\\-_'0-9]+)([^a-zA-Z\\-_'0-9]?)[^a-zA-Z\\-_'0-9]*?");
Matcher findWords = wordAndPunctuation.matcher(currentStr);
// Basically this lets us find words-in-word typos like this:
// So,bob left his clothes with me again.
// where "So,bob" becomes "So," "bob"
while (findWords.find()) {
currentStr = findWords.group(1);
currentPnc = findWords.group(2);
if (currentStr != null) {
if (observedWords.containsKey(currentStr)) {
current = observedWords.get(currentStr);
} else {
current = new ChatWord(currentStr);
observedWords.put(currentStr, current);
}
incrementWord(current);
if (currentPnc != null && !currentPnc.equals("")) {
current.addPunctuation(currentPnc.charAt(0));
}
if (prior != null) {
prior.addDescendent(current);
}
if (prior == null) {
startWord.addDescendent(current);
}
prior = current;
}
}
}
if (prior != null) { // finalize.
prior.addDescendent(ENDWORD);
}
}
/**
* Increments the value of a word (catalogues a new sighting).
*/
public void incrementWord(ChatWord word) {
Double curValue;
Double nextValue;
Collection<ChatWord> freqMap;
if (wordFrequencyLookup.containsKey(word)) {
curValue = wordFrequencyLookup.get(word);
freqMap = wordFrequency.get(curValue);
freqMap.remove(word);
} else {
curValue = 0.0;
}
nextValue=curValue+1.0;
wordFrequencyLookup.put(word, nextValue);
freqMap = wordFrequency.get(nextValue);
if (freqMap == null) {
freqMap = new HashSet<ChatWord>();
wordFrequency.put(nextValue, freqMap);
}
freqMap.add(word);
wordCount++;
wordValues++;
}
/**
* Decays a particular word by decay rate.
*/
public void decayWord(ChatWord word) {
Double curValue;
Double nextValue;
Collection<ChatWord> freqMap;
if (wordFrequencyLookup.containsKey(word)) {
curValue = wordFrequencyLookup.get(word);
freqMap = wordFrequency.get(curValue);
freqMap.remove(word);
} else {
return;
}
wordValues-=curValue; // remove old decay value
nextValue=curValue-(curValue*decayRate);
wordValues+=nextValue; // add new decay value
wordFrequencyLookup.put(word, nextValue);
freqMap = wordFrequency.get(nextValue);
if (freqMap == null) {
freqMap = new HashSet<ChatWord>();
wordFrequency.put(nextValue, freqMap);
}
freqMap.add(word);
}
/**
* Decay all word's frequency values.
*/
public void decay() {
for (ChatWord cw : wordFrequencyLookup.keySet()) {
decayWord(cw);
}
}
/**
* Gets a set of words that appear to be "top" of the frequency
* list.
*/
public Set<ChatWord> topicWords(int maxTopics) {
Set<ChatWord> topics = new HashSet<ChatWord>();
int nTopics = 0;
for (Double weight: wordFrequency.descendingKeySet()) {
for (ChatWord word: wordFrequency.get(weight)) {
topics.add(word);
nTopics++;
if (nTopics == maxTopics) {
return topics;
}
}
}
return topics;
}
/**
* Uses word frequency records to prefer to build on-topic
* sentences.
*/
public String buildSentence() {
int maxDepth = NOMINAL_LENGTH+
random.nextInt(MAX_LENGTH - NOMINAL_LENGTH);
ChatSentence cs = new ChatSentence(startWord);
// We don't want to take too long to "think of an answer"
long timeout = System.currentTimeMillis() + TIMEOUT;
double bestValue = buildSentence(cs, topicWords(TOPICS), 0.0, 0, maxDepth, timeout);
return cs.toString();
}
public double buildSentence(ChatSentence sentence,
Set<ChatWord> topics, double curValue,
int curDepth, int maxDepth, long timeout){
if (curDepth==maxDepth || System.currentTimeMillis() > timeout) {
return curValue;
}
// Determine how many branches to enter from this node
int maxBranches = MIN_BRANCHES + random.nextInt(MAX_BRANCHES - MIN_BRANCHES);
// try a few "best" words from ChatWord's descendent list.
ChatWord word = sentence.getLastWord();
NavigableMap<Integer, Collection<ChatWord>> roots =
word.getDescendents();
// Going to keep track of current best encountered sentence
double bestSentenceValue = curValue;
ChatSentence bestSentence = null;
int curBranches = 0;
for (Integer freq : roots.descendingKeySet()) {
for (ChatWord curWord : roots.get(freq)) {
if (curWord.equals(ENDWORD)) {
// let's weigh the endword cleverly
double endValue = random.nextDouble() * wordFrequency.lastKey();
if (curValue+endValue > bestSentenceValue) {
bestSentenceValue = curValue+endValue;
bestSentence = new ChatSentence(sentence);
bestSentence.addWord(curWord);
}
curBranches++;
} else {
int chance = random.nextInt(100);
boolean loop = sentence.hasWord(curWord);
/* Include a little bit of chance in the inclusion of
* any given word, whether a loop or not.*/
if ( (!loop&&chance>=SKIP_CHANCE) ||
(loop&&chance<LOOP_CHANCE)) {
double wordValue = topics.contains(curWord)?
wordFrequencyLookup.get(curWord):0.0;
ChatSentence branchSentence = new ChatSentence(sentence);
branchSentence.addWord(curWord);
addPunctuation(branchSentence);
double branchValue = buildSentence(branchSentence,
topics, curValue+wordValue, curDepth+1,
maxDepth, timeout);
if (branchValue > bestSentenceValue) {
bestSentenceValue = branchValue;
bestSentence = branchSentence;
}
curBranches++;
}
}
if (curBranches == maxBranches) break;
}
if (curBranches == maxBranches) break;
}
if (bestSentence != null) {
sentence.replaceSentence(bestSentence);
}
return bestSentenceValue;
}
/**
* Adds punctuation to a sentence, potentially.
*/
public void addPunctuation(ChatSentence sentence) {
ChatWord word = sentence.getLastWord();
NavigableMap<Integer, Collection<Character>> punc = word.getPunctuation();
if (punc.size()>0 && random.nextInt(100)<PUNCTUATION_CHANCE){
Integer puncMax = punc.lastKey();
Collection<Character> bestPunc = punc.get(puncMax);
Character puncPick = null;
for (Integer freq : punc.descendingKeySet()) {
for (Character curPunc : punc.get(freq)) {
if (random.nextInt(100)>=PUNCTUATION_SKIP_CHANCE) {
puncPick = curPunc;
break;
}
}
if (puncPick != null) break;
}
if (puncPick != null) {
sentence.addCharacter(puncPick);
}
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("ChatBrain[");
sb.append(observedWords.size());
sb.append("]:");
for (Map.Entry<String,ChatWord> cw : observedWords.entrySet()) {
sb.append("\n\t");
sb.append(wordFrequencyLookup.get(cw.getValue()));
sb.append("\t");
sb.append(cw.getValue());
}
return sb.toString();
}
}
/**
* Useful helper class to construct sentences.
*/
static class ChatSentence implements Cloneable {
/**
* List of words.
*/
private List<Object> words;
/**
* Quick search construct to have O(ln) lookup times.
*/
private Set<Object> contains;
/**
* Starts to build a sentence with a single word as anchor
*/
public ChatSentence(ChatWord anchor) {
if (anchor == null) {
throw new IllegalArgumentException("Anchor must not be null");
}
words = new ArrayList<Object>();
contains = new HashSet<Object>();
words.add(anchor);
contains.add(anchor);
}
/**
* Starts a sentence using an existing ChatSentence. Also used for
* cloning.
*/
public ChatSentence(ChatSentence src) {
words = new ArrayList<Object>();
contains = new HashSet<Object>();
appendSentence(src);
}
/**
* Adds a word to a sentence
*/
public ChatSentence addWord(ChatWord word) {
if (word == null) {
throw new IllegalArgumentException("Can't add null word");
}
words.add(word);
contains.add(word);
return this;
}
/**
* Adds a character to a sentence.
*/
public ChatSentence addCharacter(Character punc) {
if (punc == null) {
throw new IllegalArgumentException("Can't add null punctuation");
}
words.add(punc);
contains.add(punc);
return this;
}
/**
* Replace a sentence with some other sentence.
* Useful to preserve references.
*/
public ChatSentence replaceSentence(ChatSentence src) {
words.clear();
contains.clear();
appendSentence(src);
return this;
}
public ChatSentence appendSentence(ChatSentence src) {
words.addAll(src.getWords());
contains.addAll(src.getWords());
return this;
}
/**
* Get last word of the sentence.
*/
public ChatWord getLastWord() {
for (int i=words.size()-1; i>=0; i--) {
if (words.get(i) instanceof ChatWord) {
return (ChatWord) words.get(i);
}
}
throw new IllegalStateException("No ChatWords found!");
}
/**
* Checks if the sentence has a word
*/
public boolean hasWord(ChatWord word) {
return contains.contains(word);
}
/**
* Counts the number of words in a sentence.
*/
public int countWords() {
int cnt = 0;
for (Object o : words) {
if (o instanceof ChatWord) {
cnt++;
}
}
return cnt;
}
/**
* Gets all the words of the sentence
*/
private List<Object> getWords() {
return words;
}
/**
* Returns the sentence as a string.
*/
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
for (Object o : words) {
if (o instanceof ChatWord) {
ChatWord cw = (ChatWord) o;
sb.append(" ");
sb.append( cw.getWord() );
} else {
sb.append(o);
}
}
return sb.toString().trim();
}
/**
* Clones this sentence.
*/
@Override
public Object clone() {
return new ChatSentence(this);
}
}
/**
* ChatWord allows the creation of words that track how they are
* connected to other words in a forward fashion.
*/
static class ChatWord {
/** The word. */
private String word;
/** Collection of punctuation observed after this word */
private NavigableMap<Integer, Collection<Character>> punctuation;
/** Lookup linking observed punctuation to where they are in ordering */
private Map<Character, Integer> punctuationLookup;
/** Punctionation observation count */
private Integer punctuationCount;
/** Collection of ChatWords observed after this word */
private NavigableMap<Integer, Collection<ChatWord>> firstOrder;
/** Lookup linking observed words to where they are in ordering */
private Map<ChatWord, Integer> firstOrderLookup;
/** First order antecedent word count */
private Integer firstOrderCount;
/**
* Creates a new ChatWord that is aware of punctuation that
* follows it, and also ChatWords that follow it.
*/
public ChatWord(String word){
this.word = word;
this.firstOrder = new TreeMap<Integer, Collection<ChatWord>>();
this.firstOrderLookup = new HashMap<ChatWord, Integer>();
this.firstOrderCount = 0;
this.punctuation = new TreeMap<Integer, Collection<Character>>();
this.punctuationLookup = new HashMap<Character, Integer>();
this.punctuationCount = 0;
}
protected NavigableMap<Integer, Collection<ChatWord>> getDescendents() {
return firstOrder;
}
/**
* Returns how many descendents this word has seen.
*/
protected int getDescendentCount() {
return firstOrderCount;
}
/**
* Gets the lookup map for descendents
*/
protected Map<ChatWord, Integer> getDescendentsLookup() {
return firstOrderLookup;
}
/** As conversation progresses, word orderings will be encountered.
* The descendent style of "learning" basically weights how often
* words are encountered together, and is strongly biased towards
* encountered ordering.
*/
public void addDescendent(ChatWord next) {
if(next != null){
firstOrderCount++;
int nextCount = 1;
Collection<ChatWord> obs = null;
// If we've already seen this word, clean up prior membership.
if(firstOrderLookup.containsKey(next)){
nextCount = firstOrderLookup.remove(next);
obs = firstOrder.get(nextCount);
// Remove from prior obs count order
obs.remove(next);
nextCount++;
}
obs = firstOrder.get(nextCount);
if (obs == null) { // we don't have this order yet
obs = new HashSet<ChatWord>();
firstOrder.put(nextCount, obs);
}
firstOrderLookup.put(next, nextCount);
obs.add(next);
}
}
/**
* Some words have punctuation after them more often than not.
* This allows the ChatBrain to record occurrences of punctuation
* after a word.
*/
public void addPunctuation(Character punc) {
if(punc != null){
punctuationCount++;
int puncCount = 1;
Collection<Character> obs = null;
// If we've already seen this punc, clean up prior membership.
if(punctuationLookup.containsKey(punc)){
puncCount = punctuationLookup.remove(punc);
obs = punctuation.get(puncCount);
// Remove from prior obs count order
obs.remove(punc);
puncCount++;
}
obs = punctuation.get(puncCount);
if (obs == null) { // we don't have this order yet
obs = new HashSet<Character>();
punctuation.put(puncCount, obs);
}
punctuationLookup.put(punc, puncCount);
obs.add(punc);
}
}
/**
* Including this for now, but I don't like it -- it returns all
* punctuation wholesale. I think what would be better is some
* function that returns punctuation based on some characteristic.
*/
protected NavigableMap<Integer, Collection<Character>> getPunctuation() {
return punctuation;
}
/**
* Gets count of punctuation encountered.
*/
protected int getPunctuationCount() {
return punctuationCount;
}
/**
* Gets lookup of punctuations encountered.
*/
protected Map<Character, Integer> getPunctuationLookup() {
return punctuationLookup;
}
/**
* Gets the String backing this ChatWord.
*/
public String getWord() {
return word;
}
/**
* ChatWords are equivalent with the String they wrap.
*/
@Override
public int hashCode() {
return word.hashCode();
}
/**
* ChatWord equality is that ChatWords that wrap the same String
* are equal, and a ChatWord is equal to the String that it contains.
*/
@Override
public boolean equals(Object o){
if (o == this) {
return true;
}
if (o instanceof ChatWord) {
return ((ChatWord)o).getWord().equals(this.getWord());
}
if (o instanceof String) {
return ((String)o).equals(this.getWord());
}
return false;
}
/**
* Returns this ChatWord as a String.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("ChatWord[");
sb.append(word);
sb.append("]desc{");
for (Integer key : firstOrder.keySet() ) {
Collection<ChatWord> value = firstOrder.get(key);
sb.append(key);
sb.append(":[");
for (ChatWord cw : value) {
sb.append(cw.getWord());
sb.append(",");
}
sb.append("],");
}
sb.append("}punc{");
for (Integer key : punctuation.keySet() ) {
Collection<Character> value = punctuation.get(key);
sb.append(key);
sb.append(":[");
for (Character c : value) {
sb.append("\"");
sb.append(c);
sb.append("\",");
}
sb.append("],");
}
sb.append("}");
return sb.toString();
}
}
}
Przykładowa rozmowa:
Połączone b / c limitów znaków pocztowych
Rozmowa, w której Bot mówi mi, że powinienem zaprogramować żywe stworzenia
Ostatnia rozmowa, w której Bot mówi o prawdziwej naturze Chatbrains, fizyce, wszechświecie fizycznym i o tym, jak najprawdopodobniej jestem również Chatbrain
i tak dalej. Mam kilka rzeczy, które zamierzam dodać - na przykład, ze względu na powszechność prostych słów, mają one tendencję do dominacji na nieoczyszczonych listach tematów. Dodam procentowe pominięcie do słów w temacie, aby popularne słowa zostały pominięte.
Chatbot? Well the earth is fun place to talk about
- Hej, na końcu wydało tam swoje (zrozumiałe) zdanie! : D +1Chatbot? I'm not a Chatbrain since Chatbrains are the physical universe,
.The answer to the ultimate question about life, the universe, and everything is 'SyntaxError: missing ; before statement'.
C ++
Teraz muszę tylko napisać algorytm prowadzenia rozmowy. Moje pierwsze ćwiczenie uczenia maszynowego.
Edytować:
Wszystkie moje próby okazały się śmieszne, więc myślę, że tak to zostawię. Pozostałe były tak samo śmieszne, jak to:
źródło
C ++
Dążyłem do opcjonalnego bonusu 3: „ Mniej naśladować, zachowanie bota różni się od zachowania użytkownika, oddzielając postrzeganie postawy bota od postawy użytkownika. ”. Rezultatem był naprawdę uparty bot, który nie może łatwo zmienić tematu i doprowadza cię do szaleństwa.
Rozpoczęcie dyskusji zajmuje trochę czasu, po pewnym czasie dyskusja może wyglądać tak:
Podejście polega na przechowywaniu wszystkiego w grupy 3 połączonych słów. Każda grupa jest ważona i ponownie ważona w 1000-wymiarową macierz grup słów. Kod źródłowy:
źródło
class c_wglist { ... } wgl;
. Mi to pasuje. Spróbuj zainicjować zmienną wgl (klasa c_wglist) w innym miejscu.Python3 + SQLite3
Oto mały bot, który właśnie stworzyłem!
Jak to działa?
Wykorzystywane są trzy tabele SQL: jedna dla słów, jedna dla zdań, jedna do kojarzenia słów wpisanych przez użytkownika, z następnym zdaniem, który bot powinien wyświetlić.
Jakie są specjalne funkcje?
Zobacz kod poniżej:
Oto trzy pierwsze „rozmowy”, które przeprowadziłem z botem, zaczynając od pustej bazy danych:
Można rzucić okiem tutaj , aby uzyskać więcej wyjaśnień.
źródło
Oto jeden, który napisałem jakiś czas temu w Liberty BASIC. Nie uczy się, ale odpowiada na różne odpowiedzi na pytania.
przykładowa rozmowa:
źródło
HTML5
źródło
Fortran 95
Zainspirowany powyższą odpowiedzią użytkownika TheDoctor, postanowiłem stworzyć śmiesznego chatbota w podobny sposób. Ten kod również się nie uczy i udostępniam go tutaj dla zabawy.
Rozpoznaje następujące terminy i stwierdzenia: „tak” i „tak”, „nie” i „nie”, brak interpunkcji lub rodzaje znaków interpunkcyjnych (frazy kończące się na „!”, „?”, „...”) , frazy zaczynające się od „dlaczego”, „jak” lub „co”, ZWROTY W KAPSACH, śmiać się (takie jak „hahaha”, „lol” i „kkk”), bardzo krótkie i bardzo długie odpowiedzi, frazy zawierające słowo F, wyrażenie zawierające słowa „kocham cię” (spróbuj co najmniej 3 razy). Kiedy pyta o sens życia, spróbuj odpowiedzieć „42”. Jeśli zapyta, czy jest mądrzejszy niż HAL 9000, odpowiedz na coś, co zawiera słowa „prawda”, „prawda”, „poprawność”, „kłamstwo”, „fałsz” lub „nieprawda”. Jeśli zapyta, czy znasz jakiś żart, odpowiedz „nie” i pozwól mu to powiedzieć. Jeśli on „puka pukanie”, odpowiedz „kto tam jest?”, Pomóż mu również ze źródłem cytatu. Aby wyjść, po prostu wpisz „quit”.
Przykład rozmowy:
PS: proszę wybaczyć moje
goto
nadużycia, wiem, że cały ten kod jest rodzajem bałaganu ... :)źródło