Comme longtemps GNU Emacs utilisateur, je suis venu prendre C-x C-e (eval-last-sexp) pour acquis. Autrement dit, dans presque tous les fichiers que je modifie, je peux insérer une expression entre parenthèses où que je sois, appuyez sur C-x C-e, et obtenez un résultat affiché dans le mini-tampon (ou si vous programmez en Lisp, dans le REPL).
De nombreux autres langages fournissent des interfaces de console ou REPL, mais ils constituent généralement des fonctionnalités distinctes du langage, plutôt qu'un assumé mode d’interaction. J'admets qu'il s'agit d'une observation superficielle, mais on pourrait recadrer cela en termes d'approche « centrée sur le script » ou « centrée sur REPL » pour l'interaction utilisateur. Imaginez-vous en train d'apprendre un nouveau langage de programmation. Une approche « centrée sur les scripts » vous présenterait rapidement comment exécuter un programme hello world à partir d'un terminal (ou IDE), tandis qu'une approche « centrée sur REPL » vous présenterait rapidement comment accéder à un REPL et évaluer les expressions requises pour obtenir l'effet bonjour tout le monde.
En d’autres termes, une approche « centrée sur le script » sépare conceptuellement les cycles d’édition et d’évaluation, tandis que l’approche « centrée sur REPL » est conçue autour d’un flux unique d’expression, d’évaluation et de feedback. Chaque méthode a ses forces et ses faiblesses, mais pour le prototypage et l'expérimentation, il est difficile de battre le sentiment d'être « avec le code » en évaluant le code directement au niveau du curseur. Dans Emacs, pour les langages de la famille Lisp, c'est généralement la méthode d'interaction attendue.
Comment pourrions-nous le démontrer ? Eh bien, mon collègue, Mark a écrit un tutoriel sur la création d'un emploi de formation Keras en février. Le logiciel a été mis à jour depuis, nous allons donc créer une version mise à jour de ce travail ; mais au lieu du python brut, nous utiliserons hylang, qui est essentiellement un lisp pour l'interpréteur Python.
En supposant que vous disposez d'une version récente d'Emacs en cours d'exécution (disons, version 24 et supérieure) avec Emacs gestionnaire de paquets configuré, vous voudriez installer mode hy (M-x package-install RET hy-mode RET), et l'un des nombreux packages Lispy Paren, par exemple parédit, smartparens, lyspy.
Dans notre nouveau dossier, cifar10_cnn.hy, assurez-vous d'être connecté Hy mode. Utilisez ensuite M-x inferior-lisp pour démarrer une hy repl ! Faites défiler vers le bas pour une démonstration vidéo de l'interaction Emacs.
1. Tout d’abord, occupons-nous des importations
1.1Python
depuis __future__ importer print_function depuis keras.datasets importer cifar10 depuis keras.models importer séquentiel depuis keras.layers.core importer Dense, abandon, activation, aplatir depuis keras.layers.convolutional importer Convolution2D, MaxPooling2D depuis keras.optimizers importer SGD, Adadelta, Adagrad à partir de keras.utils importer np_utils, generic_utils à partir de la plage d'importation six.moves
1.2 Réécrire ceci en hy :
(import [__future__ [print_function]] [keras.datasets [cifar10]] [keras.models [Sequential]] [keras.layers.core [Dense Dropout Activation Flatten]] [keras.layers.convolutional [Convolution2D MaxPooling2D]] [keras .optimizers [SGD Adadelta Adagrad]] [keras.utils [np_utils generic_utils]] [six.moves [range]] [numpy :as np])
51 mots sur 39. Pas mal pour la concision !
2. Ensuite, réécrivons les constantes
2.1Python
batch_size = 32 nb_classes = 10 nb_epoch = 100 # dimensions de l'image d'entrée img_rows, img_cols = 32, 32 # les images CIFAR10 sont RVB img_channels = 3
2.2 Hy
(def batch_size 32 nb_classes 10 nb_epoch 100 ;; dimensions de l'image d'entrée img_rows 32 img_cols 32 ;; les images CIFAR10 sont RVB img_channels 3)
Hylang s'inspire de Clojure, mais contrairement à Clojure, permet plusieurs variables def-s par défaut. Dans ce cas, c'est très pratique.
3. Les fonctions
Note that the input_shape l'ordre des arguments a changé depuis la version précédente de (channels, rows, columns) à (rows, columns, channels)3.1Python
def load_dataset() : # les données, mélangées et réparties entre les ensembles de train et de test (X_train, y_train), (X_test, y_test) = cifar10.load_data() print('X_train shape:', X_train.shape) print(X_train. shape[0], 'train samples') print(X_test.shape[0], 'test samples') # convertir les vecteurs de classe en matrices de classes binaires Y_train = np_utils.to_categorical(y_train, nb_classes) Y_test = np_utils.to_categorical(y_test, nb_classes) X_train = X_train.astype('float32') X_test = X_test.astype('float32') X_train /= 255 X_test /= 255 return X_train, Y_train, X_test, Y_test def make_network() : model = Sequential() modèle. add(Convolution2D(32, 3, 3, border_mode='same', input_shape=(img_rows, img_cols, img_channels))) model.add(Activation('relu')) model.add(Convolution2D(32, 3, 3) ) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Dropout(0.25)) model.add(Convolution2D(64, 3, 3, border_mode= 'même')) model.add(Activation('relu')) model.add(Convolution2D(64, 3, 3)) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2 , 2))) model.add(Dropout(0.25)) model.add(Flatten()) model.add(Dense(512)) model.add(Activation('relu')) model.add(Dropout(0.5) ) model.add(Dense(nb_classes)) model.add(Activation('softmax')) return model def train_model(model, X_train, Y_train, X_test, Y_test) : sgd = SGD(lr=0.01, decay=1e-6 , momentum = 0.9, nesterov = True) model.compile (loss = 'categorical_crossentropy', optimiseur = sgd) model.fit (X_train, Y_train, nb_epoch = nb_epoch, batch_size = batch_size, validation_split = 0.1, show_accuracy = True, verbeux = 1 ) print('Testing...') res = model.evaluate(X_test, Y_test, batch_size=batch_size, verbose=1, show_accuracy=True) print('Précision du test : {0}'.format(res[1]) ) def save_model(model): model_json = model.to_json() open('cifar10_architecture.json', 'w').write(model_json) model.save_weights('cifar10_weights.h5', overwrite=True)
3.2 Hy
TRAIN et TEST en une seule étape comme ceci :(let [ [[X-train y-train] [X-test y-test]] (cifar10.load_data) ] )
(defn load-dataset [] ;; les données, mélangées et réparties entre les ensembles d'entraînement et de test (let [[TRAIN TEST] (cifar10.load_data) [X-train y-train] TRAIN [X-test y-test] TEST ;; convertir les vecteurs de classe en matrices de classes binaires Y-train (np_utils.to_categorical y-train nb_classes) Y-test (np_utils.to_categorical y-test nb_classes)] (imprimer "OK") (imprimer "X_train shape: " X-train .shape) (print (get X-train.shape 0) "train samples") (print (get X-test.shape 0) "test samples") ;; notation hy tuple (, X-train Y-train X- test Y-test))) (defn make-network [] (doto (Sequential) (.add (Convolution2D 32 3 3 :border_mode "same" :input_shape (, img_rows img_cols img_channels))) (.add (Activation "relu" )) (.add (Convolution2D 32 3 3)) (.add (Activation "relu")) (.add (MaxPooling2D :pool_size (, 2 2))) (.add (Dropout 0.25)) (.add (Convolution2D 64 3 3 :border_mode "même")) (.add (Activation "relu")) (.add (Convolution2D 64 3 3)) (.add (Activation "relu")) (.add (MaxPooling2D :pool_size (, 2 2 ))) (.add (Dropout 0.25)) (.add (Aplatir)) (.add (Dense 512)) (.add (Activation "relu")) (.add (Dropout 0.5)) (.add (Dense nb_classes )) (.add (Activation "softmax")))) (defn train-model [modèle X-train Y-train X-test Y-test] (let [sgd (SGD :lr 0.01 :decay 1e-6 :momentum 0.9 :nesterov true)] (doto model (.compile :loss "categorical_crossentropy" :optimizer sgd) (.fit X-train Y-train :nb_epoch nb_epoch :batch_size batch_size :validation_split 0.1 :show_accuracy true :verbose 1))) (imprimer "Test...") (let [res (model.evaluate X-test Y-test :batch_size batch_size :verbose 1 :show_accuracy true)] (-> "Test de précision {}" (.format (get res 1)) (print)))) (defn save-model [model] (avec [ofile (open "cifar10_architecture.json" "w")] (ofile.write (model.to_json))) (model.save_weights "cifar10_weights.h5" :écraser vrai))
Remarquez comment utiliser doto ici simplifie vraiment les affectations répétitives. Puisque les expressions sont évaluées à la forme finale qu'elles contiennent, qui est transmise par doto, notre configuration de modèle dans make-network ne nécessite même pas de nom de variable.
Hylang prend également en charge les macros de threading pratiques (->) à la clôturer, ce qui facilite l'enchaînement des instructions ; dans cet exemple, nous enfilons simplement la chaîne de format via le .format fonction, puis imprimez-le.
4. Le bloc de guidage principal
4.1Python
si __name__ == '__main__' : X_train, Y_train, X_test, Y_test = load_dataset() model = make_network() train_model(model, X_train, Y_train, X_test, Y_test) save_model(model)
4.2 Hy
(when (= --name-- "__main__") ;; ajouté pour la reproductibilité (np.random.seed 1) (laisser [[X-train Y-train X-test Y-test] (load-dataset)] ( doto (make-network) (train-model X-train Y-train X-test Y-test) (save-model))))
5. Une vidéo rapide
hylang fichier dans emacs et exécutez-le via hy REPL, en évaluant l'instruction sur le point (qui apparaît comme ^X ^E dans la superposition de touches), et autrement en basculant entre le REPL et le tampon d'édition.Vers la fin, vous pouvez voir à quel point j'ai raté le
numpy importer, a ajouté l'importation au (import ...) formulaire et a réévalué l’ensemble du formulaire d’importation. J'ai déplacé le curseur à la fin du formulaire et utilisé eval-last-sexppour évaluer l'expression, mais aurait également pu utiliser Ctrl Alt x or lisp-eval-defun, qui évaluerait l'expression de niveau supérieur entourant le curseur.Depuis que
load-dataset et make-network ne prend aucun argument, il est pratique d'envelopper les corps de fonction dans un let or doto expression (ou Formulaire), évaluant à plusieurs reprises le bloc en un point, vérifiant la sortie REPL et, une fois satisfait, encapsulant le let bloquer dans un defn formulaire.6. L'exécuter sur la plateforme
hy compilateur en exécutant
pip install git+https://github.com/hylang/hy.git
puis j'appelle simplement hy cifar10_cnn.hy pour reproduire l'exemple de sortie précédent de Mark.
(notez que dans cet exemple, nous exécutons la dernière version (version 0.11.0, commit 0abc218) de hylang directement depuis GitHub). Si vous avez installé hy en utilisant pip install hy il utilise peut-être une affectation plus ancienne let/with syntaxe.
Un exemple prêt à l'emploi peut être trouvé ici. De plus, une version tangible de cet article peut être trouvée sur github.
7. Pardonnez le jeu de mots
hy fichiers et py fichiers?